SQL - 查找来自同一字段的多个值

时间:2015-03-17 13:34:45

标签: sql sql-server-2008 tsql while-loop cursor

在下面的SQL代码中解释了问题和所需的结果。我有一个部分工作的解决方案使用游标和while循环子语句,但仍然在努力,在同一个字段中可以有任意数量的userIds(例如0-10),一些用户可能甚至没有查找表中的有效匹配。

示例数据源表。我们需要根据用户ID找到用户名

drop table #sourcetable

Create table #sourcetable (rowid int
                            ,userId varchar(50))

Insert #sourcetable
Values ('1','123456789')

Insert #sourcetable
Values ('2','123456789'+','+'456821495')

Insert #sourcetable
Values ('3','123456789'+','+'456821495'+','+'589642304')

示例查找表。我们有一个表格,其中包含按用户ID列出的所有用户

drop table #lookuptable

Create table #lookuptable (userId varchar(50)
                            ,Username varchar(100))

Insert #lookuptable
Values ('123456789','User A')

Insert #lookuptable
Values ('456821495','User B')

Insert #lookuptable
Values ('589642304','User C')

这是预期的结果。问题是源表中的1个userid字段中可能有多个(任意数量)用户ID。唯一不变的因素是有效的用户ID始终为9位数,每个用户将分别为','如果字段中有多个ID。 (SQL v2008服务器)

drop table #Resulttable

Create table #Resulttable (rowid int
                            ,userId varchar(50)
                            ,Username varchar(100))

Insert #Resulttable
Values ('1','123456789','User A')

Insert #Resulttable
Values ('2','123456789'+','+'456821495','User A,User B')

Insert #Resulttable
Values ('3','123456789'+','+'456821495'+','+'589642304','User A,User B,User C')


select * 
from #lookuptable

select * 
from #sourcetable

select * 
from #Resulttable

2 个答案:

答案 0 :(得分:0)

我建议您更改数据的存储方式。 SQL Server未优化为在单个字段中具有多个值。如果您无法更改数据的存储方式,可以通过以下方式获得所需的结果(我稍微清理了一下代码)。

IF OBJECT_ID('tempdb..#sourcetable') IS NOT NULL
    DROP TABLE #sourceTable

CREATE TABLE #sourcetable   (
                            rowid INT,
                            userId VARCHAR(50)
                            );

INSERT INTO #sourcetable
VALUES  ('1','123456789'),
        ('2','123456789'+','+'456821495'),
        ('3','123456789'+','+'456821495'+','+'589642304');

IF OBJECT_ID('tempdb..#lookuptable') IS NOT NULL
    DROP TABLE #lookupTable;

CREATE TABLE #lookuptable   (
                            userId varchar(50),
                            Username varchar(100)
                            );

INSERT INTO  #lookuptable
VALUES  ('123456789','User A'),
        ('456821495','User B'),
        ('589642304','User C');

IF OBJECT_ID('tempdb..#ResultTable') IS NOT NULL
    DROP TABLE #ResultTable

CREATE TABLE #Resulttable   (   rowid       INT,
                                userId      VARCHAR(50),
                                Username    VARCHAR(100)
                            );

INSERT INTO #Resulttable
VALUES  ('1','123456789','User A'),
        ('2','123456789'+','+'456821495','User A,User B'),
        ('3','123456789'+','+'456821495'+','+'589642304','User A,User B,User C');


WITH CTE_Results
AS
(
    select  B.rowID,
            B.userId,
            A.Username
    from #lookuptable A
    LEFT JOIN #sourcetable B
    ON CAST(B.userId AS VARCHAR(100)) LIKE '%' + CAST(A.userId AS VARCHAR(10)) + '%'
)

SELECT  rowid,
        userId,
        STUFF((
        SELECT ',' + Username
        FROM CTE_Results A
        WHERE (A.userId = B.UserID) 
        ORDER BY Username
        FOR XML PATH(''),TYPE).value('(./text())[1]','VARCHAR(MAX)')
      ,1,1,'') AS Usernames
FROM CTE_Results B
GROUP BY rowid, userId;

结果:

rowid       userId                                  Usernames
----------- -----------------------------         -----------------------
1           123456789                              User A
2           123456789,456821495                    User A,User B
3           123456789,456821495,589642304          User A,User B,User C

答案 1 :(得分:-1)

我只能同意BDL:您不应该存储这样的数据。 但有时候你别无选择,所以这里有一个有效的解决方案。

您需要做的是首先拆分userIds,然后加入,然后再次连接您的用户名。这将是:

insert into #Resulttable (rowid, userId, Username)
select rowid, userId
    , stuff(
        (select ',' + l.Username
        from #sourcetable as s
            cross apply dbo.udf_SplitWordList(s.userId,',') as split
            join #lookuptable as l
                on l.userId = split.Word
        where s.rowid = s_out.rowid
            for XML PATH(''), type).value('(./text())[1]','nvarchar(max)')
        , 1, 1, '') Username
from #sourcetable as s_out

您可以将您的分割功能[dbo]。[udf_SplitWordList]替换为您喜欢的功能。

如果你还没有,那就是我使用的那个

-- =============================================
-- Description: This function can split up a sting by a given delimiter
-- Limitations: Currently the maximum input string length is 983,040. To process longer strings, the function has to be adjusted.

-- USAGE: 
/*
    SELECT * FROM dbo.udf_SplitWordList('Hello,World',',')          -- Separater = ,
    SELECT * FROM dbo.udf_SplitWordList('Hello|World','|')          -- Separater = ,
    SELECT * FROM dbo.udf_SplitWordList('HelloWorld',CHAR(127))     -- Separater = DEL
    SELECT * FROM dbo.udf_SplitWordList('HelloWorld',CHAR(27))      -- Separater = ESC
*/
-- =============================================    
CREATE FUNCTION [dbo].[udf_SplitWordList]
(
 @list NVARCHAR(MAX)
 , @delimiter NVARCHAR(10)
)
RETURNS @t TABLE
    (
     Word NVARCHAR(MAX) NOT NULL,
     Position INT IDENTITY(1,1) NOT NULL
    )
AS 
BEGIN
    DECLARE @list_len BIGINT, @del_len INT
    SET @list_len = LEN(@list)
    SET @del_len = LEN(REPLACE(@delimiter,' ','_'))

    IF @list_len < 16
        INSERT @t
        SELECT SUBSTRING(@delimiter + @List + @delimiter, w.i + @del_len, CHARINDEX(@delimiter, @delimiter + @List + @delimiter, w.i + @del_len) - w.i - @del_len) value
        FROM (
            SELECT v0.n i
            FROM (SELECT 0 n UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION SELECT 8 UNION ALL SELECT 9 UNION ALL SELECT 10 UNION ALL SELECT 11 UNION ALL SELECT 12 UNION ALL SELECT 13 UNION ALL SELECT 14 UNION ALL SELECT 15) v0
                ) w
        WHERE w.i = CHARINDEX(@delimiter, @delimiter + @List + @delimiter, w.i) AND w.i < @list_len + @del_len
        ORDER BY i
    ELSE IF @list_len < 256
        INSERT @t
        SELECT SUBSTRING(@delimiter + @List + @delimiter, w.i + @del_len, CHARINDEX(@delimiter, @delimiter + @List + @delimiter, w.i + @del_len) - w.i - @del_len) value
        FROM (
            SELECT v0.n + v1.n i
            FROM (SELECT 0 n UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION SELECT 8 UNION ALL SELECT 9 UNION ALL SELECT 10 UNION ALL SELECT 11 UNION ALL SELECT 12 UNION ALL SELECT 13 UNION ALL SELECT 14 UNION ALL SELECT 15) v0
                , (SELECT 0 n UNION ALL SELECT 16 UNION ALL SELECT 32 UNION ALL SELECT 48 UNION ALL SELECT 64 UNION ALL SELECT 80 UNION ALL SELECT 96 UNION ALL SELECT 112 UNION SELECT 128 UNION ALL SELECT 144 UNION ALL SELECT 160 UNION ALL SELECT 176 UNION ALL SELECT 192 UNION ALL SELECT 208 UNION ALL SELECT 224 UNION ALL SELECT 240) v1
            ) w
        WHERE w.i = CHARINDEX(@delimiter, @delimiter + @List + @delimiter, w.i) AND w.i < @list_len + @del_len
        ORDER BY i
    ELSE IF  @list_len < 4096
        INSERT @t
        SELECT SUBSTRING(@delimiter + @List + @delimiter, w.i + @del_len, CHARINDEX(@delimiter, @delimiter + @List + @delimiter, w.i + @del_len) - w.i - @del_len) value
        FROM (
            SELECT v0.n + v1.n + v2.n i
            FROM (SELECT 0 n UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION SELECT 8 UNION ALL SELECT 9 UNION ALL SELECT 10 UNION ALL SELECT 11 UNION ALL SELECT 12 UNION ALL SELECT 13 UNION ALL SELECT 14 UNION ALL SELECT 15) v0
                , (SELECT 0 n UNION ALL SELECT 16 UNION ALL SELECT 32 UNION ALL SELECT 48 UNION ALL SELECT 64 UNION ALL SELECT 80 UNION ALL SELECT 96 UNION ALL SELECT 112 UNION SELECT 128 UNION ALL SELECT 144 UNION ALL SELECT 160 UNION ALL SELECT 176 UNION ALL SELECT 192 UNION ALL SELECT 208 UNION ALL SELECT 224 UNION ALL SELECT 240) v1
                , (SELECT 0 n UNION ALL SELECT 256 UNION ALL SELECT 512 UNION ALL SELECT 768 UNION ALL SELECT 1024 UNION ALL SELECT 1280 UNION ALL SELECT 1536 UNION ALL SELECT 1792 UNION SELECT 2048 UNION ALL SELECT 2304 UNION ALL SELECT 2560 UNION ALL SELECT 2816 UNION ALL SELECT 3072 UNION ALL SELECT 3328 UNION ALL SELECT 3584 UNION ALL SELECT 3840) v2
            ) w
        WHERE w.i = CHARINDEX(@delimiter, @delimiter + @List + @delimiter, w.i) AND w.i < @list_len + @del_len
        ORDER BY i
    ELSE IF  @list_len < 65536
        INSERT @t
        SELECT SUBSTRING(@delimiter + @List + @delimiter, w.i + @del_len, CHARINDEX(@delimiter, @delimiter + @List + @delimiter, w.i + @del_len) - w.i - @del_len) value
        FROM (
            SELECT v0.n + v1.n + v2.n + v3.n i
            FROM (SELECT 0 n UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION SELECT 8 UNION ALL SELECT 9 UNION ALL SELECT 10 UNION ALL SELECT 11 UNION ALL SELECT 12 UNION ALL SELECT 13 UNION ALL SELECT 14 UNION ALL SELECT 15) v0
                , (SELECT 0 n UNION ALL SELECT 16 UNION ALL SELECT 32 UNION ALL SELECT 48 UNION ALL SELECT 64 UNION ALL SELECT 80 UNION ALL SELECT 96 UNION ALL SELECT 112 UNION SELECT 128 UNION ALL SELECT 144 UNION ALL SELECT 160 UNION ALL SELECT 176 UNION ALL SELECT 192 UNION ALL SELECT 208 UNION ALL SELECT 224 UNION ALL SELECT 240) v1
                , (SELECT 0 n UNION ALL SELECT 256 UNION ALL SELECT 512 UNION ALL SELECT 768 UNION ALL SELECT 1024 UNION ALL SELECT 1280 UNION ALL SELECT 1536 UNION ALL SELECT 1792 UNION SELECT 2048 UNION ALL SELECT 2304 UNION ALL SELECT 2560 UNION ALL SELECT 2816 UNION ALL SELECT 3072 UNION ALL SELECT 3328 UNION ALL SELECT 3584 UNION ALL SELECT 3840) v2
                , (SELECT 0 n UNION ALL SELECT 4096 UNION ALL SELECT 8192 UNION ALL SELECT 12288 UNION ALL SELECT 16384 UNION ALL SELECT 20480 UNION ALL SELECT 24576 UNION ALL SELECT 28672 UNION ALL SELECT 32768 UNION ALL SELECT 36864 UNION ALL SELECT 40960 UNION ALL SELECT 45056 UNION ALL SELECT 49152 UNION ALL SELECT 53248 UNION ALL SELECT 57344 UNION ALL SELECT 61440) v3
            ) w
        WHERE w.i = CHARINDEX(@delimiter, @delimiter + @List + @delimiter, w.i) AND w.i < @list_len + @del_len
        ORDER BY i
    ELSE  
        INSERT @t
        SELECT SUBSTRING(@delimiter + @List + @delimiter, w.i + @del_len, CHARINDEX(@delimiter, @delimiter + @List + @delimiter, w.i + @del_len) - w.i - @del_len) value
        FROM (
            SELECT v0.n + v1.n + v2.n + v3.n + v4.n i
            FROM (SELECT 0 n UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION SELECT 8 UNION ALL SELECT 9 UNION ALL SELECT 10 UNION ALL SELECT 11 UNION ALL SELECT 12 UNION ALL SELECT 13 UNION ALL SELECT 14 UNION ALL SELECT 15) v0
                , (SELECT 0 n UNION ALL SELECT 16 UNION ALL SELECT 32 UNION ALL SELECT 48 UNION ALL SELECT 64 UNION ALL SELECT 80 UNION ALL SELECT 96 UNION ALL SELECT 112 UNION SELECT 128 UNION ALL SELECT 144 UNION ALL SELECT 160 UNION ALL SELECT 176 UNION ALL SELECT 192 UNION ALL SELECT 208 UNION ALL SELECT 224 UNION ALL SELECT 240) v1
                , (SELECT 0 n UNION ALL SELECT 256 UNION ALL SELECT 512 UNION ALL SELECT 768 UNION ALL SELECT 1024 UNION ALL SELECT 1280 UNION ALL SELECT 1536 UNION ALL SELECT 1792 UNION SELECT 2048 UNION ALL SELECT 2304 UNION ALL SELECT 2560 UNION ALL SELECT 2816 UNION ALL SELECT 3072 UNION ALL SELECT 3328 UNION ALL SELECT 3584 UNION ALL SELECT 3840) v2
                , (SELECT 0 n UNION ALL SELECT 4096 UNION ALL SELECT 8192 UNION ALL SELECT 12288 UNION ALL SELECT 16384 UNION ALL SELECT 20480 UNION ALL SELECT 24576 UNION ALL SELECT 28672 UNION ALL SELECT 32768 UNION ALL SELECT 36864 UNION ALL SELECT 40960 UNION ALL SELECT 45056 UNION ALL SELECT 49152 UNION ALL SELECT 53248 UNION ALL SELECT 57344 UNION ALL SELECT 61440) v3
                , (SELECT 0 n UNION ALL SELECT 65536 UNION ALL SELECT 131072 UNION ALL SELECT 196608 UNION ALL SELECT 262144 UNION ALL SELECT 327680 UNION ALL SELECT 393216 UNION ALL SELECT 458752 UNION ALL SELECT 524288 UNION ALL SELECT 589824 UNION ALL SELECT 655360 UNION ALL SELECT 720896 UNION ALL SELECT 786432 UNION ALL SELECT 851968 UNION ALL SELECT 917504 UNION ALL SELECT 983040) v4
            ) w
        WHERE w.i = CHARINDEX(@delimiter, @delimiter + @List + @delimiter, w.i) AND w.i < @list_len + @del_len
        ORDER BY i

    RETURN

END