按字符串字段排序和数字

时间:2014-11-25 20:36:28

标签: sql sql-server

我正在选择客户订单,我按customer_name订购。

客户名称很有趣,他们就是这样的

  • 客户
  • 客户#1
  • 客户#2
  • 一些客户#1
  • 一些客户#2
  • 一些客户#10
  • 一些客户#11

当我写下面的查询时

select customer_name from orders order by customer_name

这就是我得到的

  • 客户
  • 客户#1
  • 客户#2
  • 一些客户#1
  • 一些客户#10
  • 一些客户#11
  • 一些客户#2

请注意 - 一些客户#2追求 - 一些客户#11

I found this post which says to do the following:

ORDER BY 
CASE WHEN customer_name not like '%[0-9]%' THEN customer_name ELSE
   STUFF(customer_name, PATINDEX('%[0-9]%',customer_name), 0, replicate('0', 
   PATINDEX('%[0-9]%',customer_name) - len(customer_name) + PATINDEX('%[0-9]%',reverse(customer_name)) + 6))
END

最后通过数字订购是否有更好的方式?

4 个答案:

答案 0 :(得分:2)

如果所有数字都以'#'开头,那么您可以执行以下操作:

order by (case when customer_name not like '%#%' then customer_name
               else left(customer_name, charindex('#', customer_name))
          end),
         len(customer_name),
         customer_name

你可以尝试提取字符串末尾的数字(如果存在),但是如果你的问题中有格式,这应该可行。

答案 1 :(得分:2)

这是两部分答案:)

第一:正确排序

要做的第一件事是找到一种方法来规范化具有"#"的company_name值。由于这是一个字符串字段,它将根据其位置对数字进行排序,而不是基于它们的值。因此,不要将此值拆分为多个字段以放入ORDER BY,而是让客户编号的字符串表示 可排序。意思是,而" 2"自然而然地出现在" 10"在他们是字符串方面," 02"正如预期的那样来之前的" 10"看看以下内容:

SET NOCOUNT ON;
SET ANSI_NULLS ON;

-- DROP TABLE #Orders
CREATE TABLE #Orders
(
  OrderID INT IDENTITY(1, 1) NOT NULL,
  CustomerName NVARCHAR(50) NOT NULL
);

INSERT INTO #Orders (CustomerName) VALUES ('A Customer');
INSERT INTO #Orders (CustomerName) VALUES ('A Customer #1');
INSERT INTO #Orders (CustomerName) VALUES ('A Customer #2');
INSERT INTO #Orders (CustomerName) VALUES ('Some customer #1');
INSERT INTO #Orders (CustomerName) VALUES ('Some customer #2');
INSERT INTO #Orders (CustomerName) VALUES ('Some customer #10');
INSERT INTO #Orders (CustomerName) VALUES ('Some customer #11');


SELECT *,
       CASE WHEN CustomerName LIKE N'%#%' THEN
           STUFF(
                 [CustomerName],
                 (CHARINDEX(N'#', [CustomerName]) + 1), -- start just after the "#"
                 0, -- don't overwrite anything
                 REPLICATE(N'0',
                        (6 - (LEN([CustomerName]) - CHARINDEX(N'#', [CustomerName])))
                          ) -- ensure 6 digits
                )
            ELSE [CustomerName]
       END AS [SortName-test]
FROM #Orders
ORDER BY [SortName-test];

结果如您所愿。

第二:如何/在何处实施

当然,你可以总是把这个表达式放在ORDER BY中,如下所示:

SELECT *
FROM #Orders
ORDER BY
       CASE WHEN CustomerName LIKE N'%#%' THEN
           STUFF(
                 [CustomerName],
                 (CHARINDEX(N'#', [CustomerName]) + 1), -- start just after the "#"
                 0, -- don't overwrite anything
                 REPLICATE(N'0',
                        (6 - (LEN([CustomerName]) - CHARINDEX(N'#', [CustomerName])))
                          ) -- ensure 6 digits
                )
            ELSE [CustomerName]
       END;

但是,根据表的大小以及此字段在ORDER BY(或甚至是GROUP BY或任何其他需要排序的操作)中使用的频率,执行on-the -fly操作可能非常昂贵,并且无法编入索引。

在这种情况下,可以将其添加为PERSISTED COMPUTED COLUMN。因为它是"计算"它会随着基础[company_name]字段中值的任何更改而适当更改。由于它是"持久的",a)该值将存在,甚至可以被查询优化器使用,并且b)它可以被索引以获得更好的效果。

跑步:

ALTER TABLE #Orders ADD [SortName] AS (CASE WHEN CustomerName LIKE N'%#%' THEN
           STUFF(
                 [CustomerName],
                 (CHARINDEX(N'#', [CustomerName]) + 1), -- start just after the "#"
                 0, -- don't overwrite anything
                 REPLICATE(N'0',
                        (6 - (LEN([CustomerName]) - CHARINDEX(N'#', [CustomerName]))))
                          ) -- ensure 6 digits
            ELSE [CustomerName]
       END) PERSISTED;

这让你有了一个非常复杂的查询:

SELECT *
FROM #Orders
ORDER BY [SortName];

答案 2 :(得分:0)

您可以将客户名称分成两部分:前面的字符串,尾随的数字和按字符串排序的数字。

见:

SELECT *, LEFT(customer_name, LEN(customer_name) - PATINDEX('%[0-9][^0-9]%',  REVERSE(customer_name) )),
          RIGHT(customer_name, PATINDEX('%[0-9][^0-9]%',  REVERSE(customer_name) ))
FROM @Orders
ORDER BY LEFT(customer_name, LEN(customer_name) - PATINDEX('%[0-9][^0-9]%',  REVERSE(customer_name) )),
         CAST(RIGHT(customer_name, PATINDEX('%[0-9][^0-9]%',  REVERSE(customer_name) )) AS INT)

即使没有'#'在customer_name内,即使customer_name中有另一个数字,例如使用Customer 12Jonn 2 Smith #12

答案 3 :(得分:0)

试试这个。简单的方法是

ORDER  BY CASE
            WHEN customer_name NOT LIKE '%#%' THEN '0'
            WHEN customer_name LIKE '%#%' THEN LEFT(customer_name, Len(customer_name) - Charindex('#', customer_name))
          END,
          RIGHT(customer_name, Len(customer_name) - Charindex('#', customer_name))