【问题标题】:How to effectively sort nvarchar containing numbers and letters?如何有效地对包含数字和字母的 nvarchar 进行排序?
【发布时间】:2017-09-22 05:47:06
【问题描述】:

我在对表格进行有效排序时遇到了一些麻烦(表格包含大量行,因此任何优化都会产生很大的不同)。

我目前拥有的给出正确结果 (11) 的是下面的代码。

BEGIN TRANSACTION

    CREATE TABLE #tPallets
    (
      PalletNumber bigint,
      Placement nvarchar(4)
    )

    INSERT INTO #tPallets  VALUES(100000, 'B')   
    INSERT INTO #tPallets  VALUES(100001, 'M1')  
    INSERT INTO #tPallets  VALUES(100002, 'M2')  
    INSERT INTO #tPallets  VALUES(100003, 'M3')  
    INSERT INTO #tPallets  VALUES(100004, 'M4')  
    INSERT INTO #tPallets  VALUES(100005, 'M5')  
    INSERT INTO #tPallets  VALUES(100006, 'M6')  
    INSERT INTO #tPallets  VALUES(100007, 'M7')  
    INSERT INTO #tPallets  VALUES(100008, 'M8')  
    INSERT INTO #tPallets  VALUES(100009, 'M9')  
    INSERT INTO #tPallets  VALUES(100010, 'M10')  
    INSERT INTO #tPallets  VALUES(100011, 'M11')  

    SELECT 
        TOP 1 CASE 
            WHEN Placement LIKE 'M%' THEN CONVERT(int, SUBSTRING(Placement,2, len(Placement)-1)) 
            ELSE 0 
        END AS PALLET_PLACEMENT 
    FROM 
        #tPallets 
    ORDER BY 
        1 DESC

ROLLBACK TRANSACTION

因此,如果在这种特定情况下可能的话,我正在寻找一种方法来加快选择速度。


我不会认为这是答案的重复。由于线程中没有答案会使执行时间更快,而无需在 C# 中进行排序(而不是在 TSql 中进行排序)。而且我很难相信 TSql 本身没有更快的方法。

【问题讨论】:

  • 值的结构是否一致?总是 1 个字母,然后是 0 - 2 个数字?字母总是大写吗? Placement 的值是否会改变?
  • @SolomonRutzky 它总是 B 或 M1 - M150(最坏的情况)。很抱歉回答得太晚了,没有看到评论。

标签: sql-server tsql


【解决方案1】:

计算列上的索引https://docs.microsoft.com/en-us/sql/relational-databases/indexes/indexes-on-computed-columns

或者,在您的情况下,结合使用过滤索引可能会更好

https://docs.microsoft.com/en-us/sql/relational-databases/indexes/create-filtered-indexes

摆脱那些讨厌的 0。 (顺便说一下,当您的placement 不是like %M 时,您对订单的期望是什么?)

【讨论】:

  • 一开始只有一个“B”,应该被视为数字 0。因此在这种情况下查询返回 0。
  • 当然,但这与选择性有关。如果您有效地拥有ORDER BY 0,那么您将无法确定TOP 1,除非您指定其他一些标准。您将获得 a 行,但您不知道是哪一行。
  • 您用最后一条评论为我指明了正确的方向。我意识到在这种情况下 MAX() 比 TOP 1 / ORDER BY 1 DESC 快得多。
  • 虽然您可能是正确的,因为该索引可能会使此查询更快...我认为在这种特定情况下(在临时表之外)是不可取的。因为同一张表上有大量不同的查询需要其他索引。据我了解,您不希望表上有太多索引。这是正确的吗?
  • 更多索引会减慢插入和更新而不是选择的速度。您的插入和更新越慢,但如果您需要它们,那么您就需要它们。观察、衡量、决定。这总是一种权衡。
【解决方案2】:

您提供的详细信息,请尝试这种方式。

CREATE TABLE #tPallets
    (
      PalletNumber bigint,
      Placement nvarchar(4),
      OrderBy as CASE 
            WHEN Placement LIKE 'M%' THEN CONVERT(int, SUBSTRING(Placement,2, len(Placement)-1)) 
            ELSE 0 END
    )

    INSERT INTO #tPallets  VALUES(100000, 'B')   
    INSERT INTO #tPallets  VALUES(100001, 'M1')  
    INSERT INTO #tPallets  VALUES(100002, 'M2')  
    INSERT INTO #tPallets  VALUES(100003, 'M3')  
    INSERT INTO #tPallets  VALUES(100004, 'M4')  
    INSERT INTO #tPallets  VALUES(100005, 'M5')  
    INSERT INTO #tPallets  VALUES(100006, 'M6')  
    INSERT INTO #tPallets  VALUES(100007, 'M7')  
    INSERT INTO #tPallets  VALUES(100008, 'M8')  
    INSERT INTO #tPallets  VALUES(100009, 'M9')  
    INSERT INTO #tPallets  VALUES(100010, 'M10')  
    INSERT INTO #tPallets  VALUES(100011, 'M11')  

    select * from #tPallets
    order by OrderBy

或者试试这个,但不确定。

select * from #tPallets
order by BINARY_CHECKSUM(Placement) desc

【讨论】:

  • 这完全一样快,不适用于真正的查询。我正在寻找更快的 Convert / substring / len 的替代品。
  • 即使你创建计算列persisted?
  • 它没有给出我正在寻找的答案。当我在选择中添加案例而不是 * 时,它们的速度完全相同。在这种情况下,我最终想要的答案是 11。
  • @Danieboy ,我认为在计算列上创建聚集索引是唯一的方法。为什么你要一次订购这么多数据?
【解决方案3】:

使用计算列 加快速度的方法是让列保持不变,而不是在列上创建索引。请记住,这当然会对 DML 产生性能损失(插入/更新/删除)

DROP TABLE IF EXISTS #tPallets
CREATE TABLE #tPallets
(
    PalletNumber bigint,
    Placement nvarchar(4),
    PALLET_PLACEMENT AS (CASE 
        WHEN Placement LIKE 'M%' THEN CONVERT(int, SUBSTRING(Placement,2, len(Placement)-1)) 
        ELSE 0 
    END) PERSISTED /*Will save the calculation in the table*/
)

INSERT INTO #tPallets
VALUES(100000, 'B')   
,(100001, 'M1')  
,(100002, 'M2')  
,(100003, 'M3')  
,(100004, 'M4')  
,(100005, 'M5')  
,(100006, 'M6')  
,(100007, 'M7')  
,(100008, 'M8')  
,(100009, 'M9')  
,(100010, 'M10')  
,(100011, 'M11')  

/*Can create this to speed up the query*/
CREATE INDEX ix_test on #tPallets (PALLET_PLACEMENT) INCLUDE (PalletNumber,Placement)

SELECT *
FROM  #tPallets 
ORDER BY PALLET_PLACEMENT DESC

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-03-28
    • 2022-11-24
    • 2016-10-28
    • 2020-05-11
    • 2022-11-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多