我正在使用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)
我如何开始解决这个问题?
P.S。
如果@myOrderString
中的值应该分开,我可以@myOrderString = '2,1,3'
(已编辑)
编辑问题的原因:
澄清:(部分澄清基于Aaron Bertrand的评论)
2,1,3
,这意味着选择结果被重新排序:
第二行现在将作为第一行,第一行将结果
显示为秒,第3行将保持为第3行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
形式。
我尝试使用CONVERT(CHAR(1),CONVERT(VARBINARY(1),@Dec))
对结果的行号使用Dec到十六进制转换,但没有运气。
是否有简单更正可用于此工作?
为了测试超过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';
我将在答案部分使用循环提供我的完整解决方案,而不会将其视为答案。
答案 0 :(得分: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;
答案 1 :(得分: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'
我希望有一个内置函数可以将[分隔]字符串转换为执行此类操作的表。
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'
答案 2 :(得分:1)
如果你让你的字符串是commasepearated,你将它插入临时表并在该表上连接,那么临时表可以包含一个序列号,每次从逗号分隔的字符串中插入一个整数时,该序列号会递增,这就是你订购的
这是一个非常快的例子(可以提供优化的空间)
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
答案 3 :(得分: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')
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
答案 4 :(得分: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'