【问题标题】:Natural Sorting in NHibernateNHibernate 中的自然排序
【发布时间】:2015-02-27 07:30:24
【问题描述】:

我正在使用 NHibernate 在我的 c# 代码中尝试自然排序。

我正在使用以下代码在 NHibernate 中进行排序。 “代码”是“项目”表的 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...

如何用自然排序替换它?

【问题讨论】:

  • 在一个无关的注释上,我希望你知道AsQueryable() 做了什么。通常它是无用的/多于无用的。
  • 添加更多代码 sn-p 以便我们为您提供帮助。 ;)

标签: c# sql sql-server nhibernate natural-sort


【解决方案1】:

我想向您展示我们如何在这些场景中使用 NHibernate。我将使用 SQL Server 语法,但背后的想法适用于任何方言。

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>();

扩展:

应该有working life example

我创建了一个显示上述解决方案的脚本。此脚本可以针对任何 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)

试试看here

对于其他数据库引擎(或以前的版本),只需调整 SELECT 脚本,但应该清楚如何将它与 NHibernate 一起使用

SQL Server 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)

【讨论】:

  • 我为您创建了一个示例sqlfiddle.com/#!6/d41d8/24224,并扩展了答案。有适用于 SQL Server 2012 的版本和适用于 2008 年的草稿。祝你好运
  • 感谢您的示例。它在 Sql server 中工作,但这个查询不能作为你的 c# 代码工作。我正在使用 Sql server 2012。
  • 这取决于你。我给你指路了。我在 sqlfiddle 上给出了一个证明。它正在为我使用 NHibernate。现在轮到你了,找出你做错了什么。从 SQL 选择开始,确保它正在工作。接下来使用 NHibernate。祝你好运
【解决方案2】:

A) 您可以在 SQL 中执行此操作,但它变得非常复杂(因为从 NHibernate 引用 SQL 函数非常复杂,和/或从 NHiberante 使用 ROW_NUMBER() 非常复杂/几乎不可能)

B) 您可以在 SQL 的两个单独的附加列中对数据进行反规范化。写入数据时会更复杂,但排序是“免费的”:您必须创建一个 CodePrefix (varchar) 列和一个 CodeNumber (numeric nullable) 列并将拆分后的值保存在那里。

C)您已经在代码 sn-p 中实现查询(您的查询在 List&lt;Item&gt;() 中执行),并且您没有使用分页,因此您可以对 C# 端(“客户端” )。为此,您可能可以查看 Natural Sort Order in C# 。那里有各种解决方案,其中之一是http://zootfroot.blogspot.it/2009/09/natural-sort-compare-with-linq-orderby.html

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-05-05
    • 1970-01-01
    • 2012-07-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-28
    相关资源
    最近更新 更多