【问题标题】:SQL - Lookup multiple values from same fieldSQL - 从同一字段中查找多个值
【发布时间】:2015-05-19 23:07:31
【问题描述】:

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

示例数据源表。我们需要根据用户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

【问题讨论】:

  • 最初的问题是永远不应该将逗号分隔的值存储在表的字段中。你绝对应该重新考虑你的设计。
  • 我完全同意。源数据来自不是自己设计的第 3 方系统,但有人问我是否可以导入数据并为报告环境做好准备。

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


【解决方案1】:

我建议更改数据的存储方式。 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

【讨论】:

    【解决方案2】:

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

    您需要做的是首先拆分您的用户 ID,然后加入,然后再次连接您的用户名。这看起来像:

    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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-04-20
      • 2017-03-26
      • 2016-12-28
      • 2021-12-04
      • 1970-01-01
      • 2023-01-27
      • 1970-01-01
      相关资源
      最近更新 更多