I would like to show you, how we can use NHibernate in these scenarios. I will use the SQL Server syntax, but the idea behind is working in any dialect.
SQL SERVER 2012
Firstly, let's agree, that this SQL Statement will give us the NUMBER part:
ISNULL(TRY_PARSE(SUBSTRING(Code, (PATINDEX('%[0-9]%', Code)), LEN(Code)) as int), 0)
AS [NUM]
It does finds the char where is first number 0-9 and, takes it as a substring and call try_parse
to get number (or 0 if ISNULL)
This could extract text part, and handle scenarios where the number is missing (e.g. value 'codeX' instead of 'code0')
LEFT(Code, IIF( PATINDEX('%[0-9]%', Code) > 0, PATINDEX('%[0-9]%', Code) - 1, 0))
AS [TXT]
And now we can put that all togehter and with projections have this query, which is doing natural sort
// text part as projection
var textPart = Projections.SqlProjection(
"LEFT(Code, IIF( PATINDEX('%[0-9]%', Code) > 0, PATINDEX('%[0-9]%', Code) - 1, 0)) AS [TXT]"
, new string[] {}
, new IType[] {}
);
// number part as projection
var numberPart = Projections.SqlProjection(
" ISNULL(TRY_PARSE(SUBSTRING(Code, (PATINDEX('%[0-9]%', Code)), LEN(Code)) as int), 0) AS [NUM]"
, new string[] {}
, new IType[] {}
);
;
var criteria = session
.CreateCriteria<Item>()
.Add(Restrictions.InsensitiveLike("Code", code + "%"));
var items = criteria
// here we order by our projectons
.AddOrder(Order.Asc(textPart))
.AddOrder(Order.Asc(numberPart))
.List<Item>();
EXTEND:
There should be working life example
I created a script showing the solution described above. This script can be run against any MS SQL Server 2012 DB:
IF OBJECT_ID('[dbo].[MyTable]', 'U') IS NOT NULL
DROP TABLE [dbo].[MyTable]
CREATE TABLE [dbo].[MyTable] (
[My_ID] [int] IDENTITY(1,1) NOT NULL,
[Code] [varchar](255) NOT NULL,
CONSTRAINT [PK_My_ID] PRIMARY KEY CLUSTERED
(
[My_ID] ASC
)
)
INSERT INTO [dbo].[MyTable] ([Code])
VALUES ('Code1')
, ('Code2')
, ('Code10')
, ('Code20')
, ('Code100')
, ('Code200')
-- SELECT [Code] FROM [dbo].[MyTable] ORDER BY [Code]
SELECT [Code] FROM [dbo].[MyTable] ORDER BY
LEFT(Code, IIF( PATINDEX('%[0-9]%', Code) > 0, PATINDEX('%[0-9]%', Code) - 1, 0))
,
ISNULL(TRY_PARSE(SUBSTRING(Code, (PATINDEX('%[0-9]%', Code)), LEN(Code)) as int), 0)
Try to check it here
For Other DB engines (or preve version), just adjust the SELECT script, but idea how to use it with NHibernate should be clear
SQL Server 2008
There is a small draft of a version which could be used for sql server 2008
SELECT [Code] FROM [dbo].[MyTable] ORDER BY
LEFT(Code, (CASE WHEN PATINDEX('%[0-9]%', Code) > 0 THEN PATINDEX('%[0-9]%', Code) - 1 ELSE 0 END) )
,
ISNULL(CONVERT(decimal(18,3), (SUBSTRING(Code, (PATINDEX('%[0-9]%', Code)), LEN(Code)))), 0)