NHibernate 中的自然排序
Natural Sorting in NHibernate
我正在尝试使用 NHibernate 在我的 C# 代码中进行自然排序。
我正在使用以下代码在 NHibernate 中进行排序。 "Code" 是 "Item" table 的 nvarchar 字段。
public IQueryable<Item> SearchItems(string code)
{
var session = GetSession();
var criteria = session.CreateCriteria(typeof(Item)).Add(Restrictions.InsensitiveLike("Code", code + "%"));
var items = criteria.AddOrder(Order.Asc("Code")).List<Item>().AsQueryable();
return items;
}
使用上面的代码我得到类似
的输出
item1,
item10,
item100,
item2,
item20, ...
但我想要
item1,
item2,
...,
item10,
...,
item20,
...,
item100...
如何将其替换为自然排序?
A) 你可以在 SQL 中完成它,但它变得相当复杂(因为从 NHibernate 引用 SQL 函数非常复杂,and/or 它相当 complex/nearly 无法使用来自 NHiberante 的 ROW_NUMBER())
B) 您可以在 SQL 中的两个单独的附加列中对数据进行反规范化。当你写入数据时,做起来更复杂,但是排序是 "free":你必须创建一个 CodePrefix (varchar) 列和一个 CodeNumber (numeric nullable) 列,并在其中保存拆分后的值。
C) 您已经在您的代码片段中具体化查询(您的查询在 List<Item>()
中执行),并且您没有使用分页,因此您可以对 C# 端的项目进行排序("client side").为此,您可能可以查看 Natural Sort Order in C# . There are various solutions there, one of which is http://zootfroot.blogspot.it/2009/09/natural-sort-compare-with-linq-orderby.html
我想向您展示我们如何在这些场景中使用 NHibernate。我将使用 SQL 服务器语法,但背后的想法适用于任何方言。
SQL 服务器 2012
首先,让我们同意,这个 SQL 语句将给我们 NUMBER 部分:
ISNULL(TRY_PARSE(SUBSTRING(Code, (PATINDEX('%[0-9]%', Code)), LEN(Code)) as int), 0)
AS [NUM]
它确实找到第一个数字 0-9 的字符,并将其作为子字符串并调用 try_parse
以获取数字(如果 ISNULL 则为 0)
这可以提取文本部分,并处理缺少数字的情况(例如值 'codeX' 而不是 'code0')
LEFT(Code, IIF( PATINDEX('%[0-9]%', Code) > 0, PATINDEX('%[0-9]%', Code) - 1, 0))
AS [TXT]
现在我们可以将所有这些放在一起并与投影一起使用此查询,它正在执行 自然排序
// 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>();
延长:
我创建了一个显示上述解决方案的脚本。此脚本可以 运行 针对任何 MS SQL 服务器 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)
试试看here
对于其他数据库引擎(或之前的版本),只需调整 SELECT 脚本,但应该清楚如何与 NHibernate 一起使用它
SQL 服务器 2008
有一个版本的小草稿可以用于 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)
我正在尝试使用 NHibernate 在我的 C# 代码中进行自然排序。
我正在使用以下代码在 NHibernate 中进行排序。 "Code" 是 "Item" table 的 nvarchar 字段。
public IQueryable<Item> SearchItems(string code)
{
var session = GetSession();
var criteria = session.CreateCriteria(typeof(Item)).Add(Restrictions.InsensitiveLike("Code", code + "%"));
var items = criteria.AddOrder(Order.Asc("Code")).List<Item>().AsQueryable();
return items;
}
使用上面的代码我得到类似
的输出item1,
item10,
item100,
item2,
item20, ...
但我想要
item1,
item2,
...,
item10,
...,
item20,
...,
item100...
如何将其替换为自然排序?
A) 你可以在 SQL 中完成它,但它变得相当复杂(因为从 NHibernate 引用 SQL 函数非常复杂,and/or 它相当 complex/nearly 无法使用来自 NHiberante 的 ROW_NUMBER())
B) 您可以在 SQL 中的两个单独的附加列中对数据进行反规范化。当你写入数据时,做起来更复杂,但是排序是 "free":你必须创建一个 CodePrefix (varchar) 列和一个 CodeNumber (numeric nullable) 列,并在其中保存拆分后的值。
C) 您已经在您的代码片段中具体化查询(您的查询在 List<Item>()
中执行),并且您没有使用分页,因此您可以对 C# 端的项目进行排序("client side").为此,您可能可以查看 Natural Sort Order in C# . There are various solutions there, one of which is http://zootfroot.blogspot.it/2009/09/natural-sort-compare-with-linq-orderby.html
我想向您展示我们如何在这些场景中使用 NHibernate。我将使用 SQL 服务器语法,但背后的想法适用于任何方言。
SQL 服务器 2012
首先,让我们同意,这个 SQL 语句将给我们 NUMBER 部分:
ISNULL(TRY_PARSE(SUBSTRING(Code, (PATINDEX('%[0-9]%', Code)), LEN(Code)) as int), 0)
AS [NUM]
它确实找到第一个数字 0-9 的字符,并将其作为子字符串并调用 try_parse
以获取数字(如果 ISNULL 则为 0)
这可以提取文本部分,并处理缺少数字的情况(例如值 'codeX' 而不是 'code0')
LEFT(Code, IIF( PATINDEX('%[0-9]%', Code) > 0, PATINDEX('%[0-9]%', Code) - 1, 0))
AS [TXT]
现在我们可以将所有这些放在一起并与投影一起使用此查询,它正在执行 自然排序
// 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>();
延长:
我创建了一个显示上述解决方案的脚本。此脚本可以 运行 针对任何 MS SQL 服务器 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)
试试看here
对于其他数据库引擎(或之前的版本),只需调整 SELECT 脚本,但应该清楚如何与 NHibernate 一起使用它
SQL 服务器 2008
有一个版本的小草稿可以用于 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)