0

我正在使用 SQL Server 2008 R2。

我想按我作为字符串保存的特定顺序对结果进行排序

(字符串是以编程方式创建的)。

考虑这张表:

Col1    Col2    Col3
1       Jon     a
2       Joan    b
3       John    a
4       Jonnie  b
5       Jonny   a

我有一个nvarchar声明为 的变量@myOrderString,其中包含我要选择的行的顺序。

让我们这么说@myOrderString = '213'(编辑)

所以,我想做这样的事情:

SELECT 
    ROW_NUMBER() OVER (ORDER BY @mySortString) AS Row, 
    ( Col2 + '(' + Col1 + ')' ) AS Outcome
FROM 
    myTable 
WHERE 
    Col3 = 'a' 
ORDER BY 
    @mySortString

为了得到结果:(已编辑)

Row Outcome
1   John (3)
2   Jon (1)
3   Jonny (5)

我什至如何开始解决这个问题?

附言

如果中的值@myOrderString应该分开,我可以做到@myOrderString = '2,1,3'(编辑)


编辑问题的原因:

  1. 字符串内容有错误。[标记为(已编辑)]
  2. 结果出现错误。感谢 Andriy M. [标记为(已编辑)]
  3. 我添加了外观更改并更正了一些语法和拼写错误。

澄清:(此澄清的一部分基于 Aaron Bertrand 的评论)

  1. 字符串中的值数始终与结果中的行数匹配。
  2. 字符串中的值始终是从 1 到行数的数字。
  3. 第一个结果的行标记为 1;第二个标记为 2,第三个标记为 3。现在,该字符串用于对结果进行重新排序。当字符串2,1,3为时表示选择结果被重新排序:第二行现在将作为第一行,第一个结果将显示为第二,第三行将保持第三
  4. 最大行数为 15,因此字符串可以是形式123456789ABCDEF,或者如果这种简化形式的解决方案不简单,则字符串可以是形式1,2,3,4,5,6,7,8,9,10,11,12,13,14,15

感谢这里和那里的许多人,我已经达到了以下解决方案:

(不需要函数或循环)

-- creating the original table and filling it
DECLARE @t TABLE(Col1 INT, Col2 VARCHAR(22), Col3 CHAR(1));

INSERT @t VALUES
(1,'Jon',   'a'),
(2,'Joan',  'b'),
(3,'John',  'a'),
(4,'Jonnie','b'),
(5,'Jonny', 'a');

-- this is the required order of the results
DECLARE @myOrderString VARCHAR(32) = '213';

-- this is my current solution
SELECT 
    ROW_NUMBER()OVER (ORDER BY CHARINDEX(CAST(rr AS NVARCHAR(MAX)), @myOrderString)) As [Row],
    Outcome 
FROM 
    (
        SELECT 
            ROW_NUMBER()OVER (ORDER BY Col1) AS rr, 
            Col2 + ' (' + CONVERT(NVARCHAR(22),Col1)+ ')' AS Outcome 
        FROM @t 
        WHERE  Col3 = 'a'
    ) as r
ORDER BY 
    [Row]

但是,它最多可以处理 9 行结果,而我最多可以使用 15 行来表示表单123456789ABCDEF

我尝试通过使用对结果的行号使用 Dec 到 Hex 转换CONVERT(CHAR(1),CONVERT(VARBINARY(1),@Dec)),但没有运气。

是否有一个简单的更正可以使它起作用?


一个长示例(超过 9 行)

为了测试超过 9 行的示例,我使用以下命令:

-- creating the original table and filling it
DECLARE @t TABLE(Col1 INT, Col2 VARCHAR(22), Col3 CHAR(1));

INSERT @t VALUES
(1,'Jon',   'a'),
(2,'Joan',  'b'),
(3,'John',  'a'),
(4,'Jonnie','b'),
(5,'Jonny', 'a'),
(6,'Don',   'a'),
(7,'Doan',  'b'),
(8,'Dohn',  'a'),
(9,'Donnie','b'),
(10,'Donny', 'a'),
(11,'Gon',   'a'),
(12,'Goan',  'a'),
(13,'Gohn',  'a'),
(14,'Gonnie','a'),
(15,'Gonny', 'a');

-- this is the required order of the results
DECLARE @myOrderString VARCHAR(32) = '456B213A789';

一个完整的解决方案:

我将在这里使用答案部分的循环提供我的完整解决方案,而不将其作为答案。

4

5 回答 5

3

使您的字符串以逗号分隔会好得多。您可以使用这样的拆分功能:

CREATE FUNCTION dbo.SplitInts
(
    @List       NVARCHAR(MAX),
    @Delimiter  NVARCHAR(255)
)
RETURNS TABLE
AS
    RETURN (SELECT Number = ROW_NUMBER() OVER (ORDER BY Number),
        Item FROM (SELECT Number, Item = LTRIM(RTRIM(SUBSTRING(@List, Number, 
        CHARINDEX(@Delimiter, @List + @Delimiter, Number) - Number)))
    FROM (SELECT ROW_NUMBER() OVER (ORDER BY [object_id])
      FROM sys.all_objects) AS n(Number)
      WHERE Number <= CONVERT(INT, LEN(@List))
      AND SUBSTRING(@Delimiter + @List, Number, 1) = @Delimiter
    ) AS y
    WHERE ISNUMERIC(Item) = 1
  );
GO

现在你可以这样做:

DECLARE @myOrderString VARCHAR(32) = '2,1,3';

DECLARE @t TABLE(col1 INT, col2 VARCHAR(32), col3 CHAR(1));

INSERT @t VALUES
(1,'Jon',   'a'),
(2,'Joan',  'b'),
(3,'John',  'a'),
(4,'Jonnie','b'),
(5,'Jonny', 'a');

SELECT 
  [Row] = s.Number, 
  Outcome = t.col2 + ' (' + CONVERT(VARCHAR(12), t.col1) + ')' 
FROM 
(
  SELECT col1, col2, rn = ROW_NUMBER() OVER (ORDER BY col1)
  FROM @t WHERE col3 = 'a'
) AS t 
INNER JOIN 
  dbo.SplitInts(@myOrderString, ',') AS s
  ON s.Item = t.rn 
ORDER BY s.Number;

再次,结果:

Row  Outcome
--   ----------
1    John (3)
2    Jon (1)
3    Jonny (5)

编辑

这是一个不使用函数的版本(尽管我不确定为什么在这种情况下如此令人反感),不需要列表以逗号分隔(根据OP 的自我回答),并且不必在继续之前在循环中手动填充表变量(这会导致整体计划的成本更低,即使当您忽略循环时 OP 答案中的最终查询确实看起来更便宜)插入)。这将返回与 OP 的自我回答相同的结果(给定新的“长示例”示例数据添加到问题中),但同样限制为 15 个最大排序值。

;WITH n(n,c) AS 
(
  SELECT CASE WHEN n < 10 THEN n ELSE n -7 END, CHAR(n+48)
  FROM 
  (
    SELECT TOP (21) n = ROW_NUMBER() OVER (ORDER BY [object_id])
    FROM sys.objects ORDER BY [object_id]
  ) AS x WHERE n BETWEEN 1 AND 9 OR n BETWEEN 17 AND 21
),
x(Outcome, n) AS 
(
  SELECT col2 + ' (' + CONVERT(VARCHAR(12), col1) + ')', 
  ROW_NUMBER() OVER (ORDER BY col1)
  FROM (SELECT col1, col2 FROM @t WHERE col3 = 'a') AS y
)
SELECT [Row] = ROW_NUMBER() OVER (ORDER BY 
    COALESCE(NULLIF(CHARINDEX(n.c, @myOrderString), 0), 16)), 
  Outcome FROM x LEFT OUTER JOIN n ON x.n = n.n
ORDER BY [Row], Outcome;
于 2012-06-16T19:15:00.437 回答
2

我是这个问题的作者。我不认为这个答案是答案。

该解决方案基于对每个字符进行循环@myOrderString

这里给出这个解决方案是为了启发,希望能根据我在问题中描述的解决方案找到一个解决方案。

创建原始表并填充它:

DECLARE @t TABLE(Col1 INT, Col2 VARCHAR(32), Col3 CHAR(1));

INSERT @t VALUES
(1,'Jon',   'a'),
(2,'Joan',  'b'),
(3,'John',  'a'),
(4,'Jonnie','b'),
(5,'Jonny', 'a'),
(6,'Don',   'a'),
(7,'Doan',  'b'),
(8,'Dohn',  'a'),
(9,'Donnie','b'),
(10,'Donny', 'a'),
(11,'Gon',   'a'),
(12,'Goan',  'a'),
(13,'Gohn',  'a'),
(14,'Gonnie','a'),
(15,'Gonny', 'a')

这是所需的结果顺序

DECLARE @myOrderString VARCHAR(32) = '456B213A789'

使用 Order Colomn (OC) 将字符串转换为表格

我希望有一个内置函数可以将 [delimited] 字符串转换为执行类似操作的表。

DECLARE @ot TABLE (PK INT IDENTITY(1,1) PRIMARY KEY CLUSTERED, OC INT)
DECLARE @i int
SET @i = 0
WHILE @i < LEN(@myOrderString) 
    BEGIN
        SET @i = @i + 1
        IF ASCII(UPPER(SUBSTRING(@myOrderString,@i,1))) < 65
            INSERT @ot VALUES (SUBSTRING(@myOrderString,@i,1))
        ELSE
            INSERT @ot VALUES (ASCII(UPPER(SUBSTRING(@myOrderString,@i,1)))-55)
    END

按要求的顺序呈现结果:

SELECT 
    ROW_NUMBER() OVER (ORDER BY PK) AS [Row], 
    r.Outcome 
FROM @ot 
INNER JOIN (
    SELECT 
        ROW_NUMBER() OVER (ORDER BY Col1) AS [RRow], 
        Col2 +' (' + CONVERT(varchar(11), Col1) + ')' AS Outcome
    FROM @t 
    WHERE Col3 = 'a') AS r
ON OC=RRow

附录

对于一个有趣的结果(选择结果的行 - 不需要)尝试设置@myOrderString = '333222111'

于 2012-06-17T12:49:03.727 回答
1

如果您将字符串逗号分隔,则将其插入临时表并加入该表,然后临时表可以包含一个序列号,每次您从逗号分隔字符串中插入一个整数时,该序列号都会递增,并且是你订购的

这是一个非常快速的例子(可能有优化的空间)

DECLARE @string varchar(max),
    @delimiter char(1),
    @xml xml

SELECT @string = '3,1,4',
    @delimiter= ','

SELECT @xml = CONVERT(xml,'<root><s>' + REPLACE(@string,@delimiter,'</s><s>') + '</s></root>')

create table #values
(
    seq integer identity(1, 1),
    value integer
)

insert into #values (value)
SELECT [Value] = T.c.value('.','varchar(20)')
FROM @xml.nodes('/root/s') T(c)

select
    v.seq,
    m.Col2,
    m.Col1
from dbo.myTable m
inner join #values v on m.Col1 = v.value
order by v.seq

drop table #values
于 2012-06-16T19:02:44.147 回答
1

根据要求,下面提到的是解决方案......

Select col1, Col2 + ' (' + Convert(Varchar, col1) + ')'
From #T
Where CHARINDEX(CAST(Col1 AS NVARCHAR(MAX)), '3,1,4') <> 0
order by CHARINDEX(CAST(Col1 AS NVARCHAR(MAX)), '3,1,4')

编辑 -2 UDF 中忽略了非数值。

编辑 - 1 支持大于 9 的值

declare @myTable table(col1 int, col2 varchar(10), col3 varchar(1))

insert @myTable values
(1,'Jon',   'a'),
(2,'Joan',  'b'),
(322,'John',  'a'),
(4,'Jonnie','b'),
(5,'Jonny', 'a')

Select col1, Col2 + ' (' + Convert(Varchar, col1) + ')'
From @myTable T
INNER JOIN 
(
    Select * From dbo.Split('322,1,4', ',')
)K
ON K.val = T.col1
Order by K.id

用户定义函数

CREATE FUNCTION [dbo].[Split](@String varchar(8000), @Delimiter char(1))       
returns @temptable TABLE (id int IDENTITY(1,1), Val Int)       
as       
begin       
    declare @idx int       
    declare @slice varchar(8000)       

    select @idx = 1       
        if len(@String)<1 or @String is null  return       

    while @idx!= 0       
    begin       
        set @idx = charindex(@Delimiter,@String)       
        if @idx!=0       
            set @slice = left(@String,@idx - 1)       
        else       
            set @slice = @String       

        if(isnumeric(@slice) = 0) 
        Set @slice = '';

        if(len(@slice)>0)  
            insert into @temptable(Val) values(@slice)       

        set @String = right(@String,len(@String) - @idx)       
        if len(@String) = 0 break       
    end   
return       
end  
于 2012-06-16T19:25:02.033 回答
0

鉴于提供的数据,您的输出表是不可能的,因为 Jonnie 是“b”而不是“a”。此外,在下面的示例中,'Jonny' 是一个 'a' 但不会返回,因为 5 不在提供的排序字符串中。需要一些关于如何处理这样的行的指导。

declare @sortTable table (sortId int identity, sortOrder varchar(1))
declare @sortString nvarchar(100) = '314'

declare @position int = 1
declare @sortOrder varchar(1) = substring(@sortString,@position,1)
while @sortOrder != ''
begin
    insert @sortTable (sortOrder) values (@sortOrder)
    set @position = @position + 1
    set @sortOrder = SUBSTRING(@sortString,@position,1)
end


declare @myTable table(col1 varchar(1), col2 varchar(10), col3 varchar(1))

insert @myTable values
(1,'Jon',   'a'),
(2,'Joan',  'b'),
(3,'John',  'a'),
(4,'Jonnie','b'),
(5,'Jonny', 'a')

SELECT ROW_NUMBER() OVER (ORDER BY rsSort.SortID) AS Row, ( Col2 + '(' + Col1 + ')' ) AS Outcome
FROM @myTable rsMain
inner join @sortTable rsSort on rsSort.sortOrder = rsMain.col1
WHERE Col3 = 'a'

工作示例

于 2012-06-16T19:28:00.313 回答