Suppose I declare a list of integer as following in SQL.
DECLARE integerlist varchar(max) = '45, 3, 23, 100'
How could i make an efficient query to output as follow (in which the number is sort in ascending order)
integerlist = '3, 23, 45, 100'
Suppose I declare a list of integer as following in SQL.
DECLARE integerlist varchar(max) = '45, 3, 23, 100'
How could i make an efficient query to output as follow (in which the number is sort in ascending order)
integerlist = '3, 23, 45, 100'
I suggest conversion of the list simply by constructing a sql query of this list and execute this query with required order by, then reconstruct string back!
DECLARE @list varchar(max) = '45, 3, 23, 100', @sql nvarchar(max)
DECLARE @A AS TABLE (A int)
set @sql='SELECT ' + REPLACE(@list, ',', ' A UNION ALL SELECT ') + ' A ORDER BY A'
INSERT into @A EXECUTE sp_executesql @sql
SET @list=null
SELECT @list = COALESCE(@list + ', ', '') + cast(A as varchar) FROM @A
SELECT @list
Demo: http://sqlfiddle.com/#!3/9eecb7db59d16c80417c72d1/4892
Update: Using UNION ALL instead of UNION will boost optimization about 3 times for large numbers
SQL server is not the best place to do this. Seriously. Being that said, one approach is xml:
DECLARE @list varchar(max) = '45, 3, 23, 100';
DECLARE @myDoc xml = '<a>' + replace(@list, ', ','</a><a>') + '</a>';
SET @list = (SELECT left(name, len(name)-1) FROM
(SELECT T.X.value('.[1]', 'varchar(max)') +', '
FROM @myDoc.nodes('/a') as T(X)
ORDER BY T.X.value('.[1]', 'int') FOR XML PATH('')) as xmlPath(name));
SELECT @list;
EDIT
To show how bad it is to do it on sql server, I made a bechmark
Numbers |UNION ALL |XML |C#
-------------------------------------
1 |5 ms |7 ms |214 ms
10 |5 ms |6 ms |7 ms
100 |8 ms |8 ms |2 ms
1000 |57 ms |23 ms |4 ms
10000 |2,4 sec |172 ms |12 ms
100000 |13 min |1,8 sec |86 ms
1000000 |N/A |18 sec |877 ms
10000000|N/A |3,2 min |11 sec
I made it quite unfair for C# - included read from database and write to disc to the time, elapsed first run, single threaded processing. The value for one number is off probably because of JIT, but aside from this the winner is clear. My configuration is SQL server 2014 on i5-3570K.
Here is full code
--------------------------------------------
-- Just table of numbers 1, 2, 3, 4, 5,...
--------------------------------------------
CREATE TABLE Tally (Number int not null);
;WITH
Pass0 as (select 1 as C union all select 1), --2 rows
Pass1 as (select 1 as C from Pass0 as A, Pass0 as B),--4 rows
Pass2 as (select 1 as C from Pass1 as A, Pass1 as B),--16 rows
Pass3 as (select 1 as C from Pass2 as A, Pass2 as B),--256 rows
Pass4 as (select 1 as C from Pass3 as A, Pass3 as B),--65536 rows
Pass5 as (select 1 as C from Pass3 as A, Pass4 as B),--16M rows
T as (select row_number() over(order by C) as Number from Pass5)
INSERT Tally (Number)
SELECT Number
FROM T
WHERE Number <= 10000000;
ALTER TABLE Tally ADD CONSTRAINT PK_Tally PRIMARY KEY CLUSTERED (Number);
--------------------------------------------
-- Each varchar constains string of random numbers smaller than 1000000
-- separated by ", ". Count of random numbers is txt_num.
--------------------------------------------
CREATE TABLE [dbo].[Texts](
[txt_num] [int] NOT NULL,
[txt_text] [varchar](max) NOT NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
--------------------------------------------
-- Filling of the table
--------------------------------------------
INSERT INTO Texts (txt_num, txt_text)
SELECT txt_num, txt_text
FROM
(SELECT txt_num, (SELECT left(name, len(name)-1) as txt_text FROM
(SELECT convert(varchar(max), convert(int, 1000000.0*RAND(convert(varbinary, newid()))))+', '
FROM Tally WHERE Number<=txt_num FOR XML PATH('')) as xmlPath(name)) txt_text
FROM (VALUES (1), (10), (100), (1000), (10000), (100000), (1000000), (10000000)) AS Tmp(txt_num))Tmp3
--------------------------------------------
-- UNION ALL function
--------------------------------------------
DECLARE @list varchar(max);
SELECT @list = (SELECT txt_text FROM Texts WHERE txt_num=1);--10,100,1000,...
DECLARE @sql nvarchar(max);
DECLARE @A AS TABLE (A int)
SET @sql='SELECT ' + REPLACE(@list, ',', ' A UNION ALL SELECT ') + ' A ORDER BY A'
INSERT into @A EXECUTE sp_executesql @sql
SET @list=null
SELECT @list = COALESCE(@list + ', ', '') + cast(A as varchar) FROM @A
SELECT @list
--------------------------------------------
-- XML function
--------------------------------------------
DECLARE @list varchar(max);
SELECT @list = (SELECT txt_text FROM Texts WHERE txt_num=1);--10,100,1000,...
DECLARE @myDoc xml = '<a>' + replace(@list, ', ','</a><a>') + '</a>';
SET @list = (SELECT left(name, len(name)-1) FROM
(SELECT T.X.value('.[1]', 'varchar(max)') +', '
FROM @myDoc.nodes('/a') as T(X)
ORDER BY T.X.value('.[1]', 'int') FOR XML PATH('')) as xmlPath(name));
SELECT @list;
--------------------------------------------
-- C# code
--------------------------------------------
static void Main(string[] args)
{
using (var dc = new DataClasses1DataContext())
{
for (int i = 1; i <= 10000000; i *= 10)
{
var timer = System.Diagnostics.Stopwatch.StartNew();
string input = dc.Texts
.Where(w => w.txt_num == i)
.Select(s => s.txt_text)
.Single();
string output = string.Join(", ", input
.Split(", ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries)
.Select(s => int.Parse(s))
.OrderBy(o => o)
);
System.IO.StreamWriter sw = new System.IO.StreamWriter("cs" + i);
sw.WriteLine(output);
sw.Close();
Console.WriteLine(i + " - " + timer.ElapsedMilliseconds + " miliseconds");
}
Console.ReadKey();
}
}