【问题标题】:SQL select joined string for set of idsSQL 为一组 id 选择连接字符串
【发布时间】:2009-07-15 14:16:52
【问题描述】:

我必须处理一个 mssql 数据库和这样的表中给出的信息:

Users:

ID Name    Countries
--------------------
1  User1   1,2,3
2  User2   2,5

Countries:
ID Country
----------
1  Australia
2  Germany
3  USA
4  Norway
5  Canada

现在,我正在寻找的是一个 select 语句,它会给我这样的结果:

Result:
ID User   CountriesByName
-----------------------------
1  User1  Australia, Germany, USA
2  User2  Germany, Canada

我更喜欢一种不依赖于特殊 MSSQL 语法的解决方案,但我无法使用一些 LINQ 魔法:(

【问题讨论】:

  • 我认为您需要先将 Country 字段标准化为连接表“UserCountry”。
  • 很遗憾我无法更改数据库方案

标签: sql sql-server select join


【解决方案1】:

首先,您需要拆分该字符串。这是一个可行的拆分功能:

Create Function [dbo].[split]
    (@input   varChar(8000)        -- List of delimited items
    ,@delimit varChar(8000) = ',') -- delimiter that separates items
Returns @List Table ([item] varChar(8000)) As
Begin

Declare @item VarChar(8000);

while charIndex(@delimit, @input, 0) <> 0 Begin

    Select
        @item  = rTrim(lTrim(subString(@input, 1, charIndex(@delimit, @input, 0) - 1))),
        @input = rTrim(lTrim(subString(@input, charIndex(@delimit, @input, 0) + Len(@delimit), Len(@input))));

    If Len(@item) > 0 Insert Into @List Select @item

End

If Len(@input) > 0 Insert Into @List Select @input

Return;

End

然后,您需要将这些值加入您的国家/地区表中,然后重新加入它们。这将使您获得最大的收益:

Select ID
      ,[Name] as [User]
      ,(
        Select [country] + ', '
        From [Countries]
        Where [ID] In (
            Select Cast([item] As Integer)
            From dbo.split(U.Countries, ',')
            Where IsNumeric(item) = 1)
        Order By [country]
        For XML Path('')) As [CountriesByName]
From [Users] As U

但是,这会留下一个逗号。您可能想在其他层上删除它,但以防万一您必须在 SQL 中执行此操作,这应该可以:

Select ID
      ,[User]
      ,Left([CountriesByName], Len([CountriesByName]) - 1) As [CountriesByName]
From (
    Select ID
          ,[Name] as [User]
          ,(
            Select [country] + ', '
            From [Countries]
            Where [ID] In (
                Select Cast([item] As Integer)
                From dbo.split(U.Countries, ',')
                Where IsNumeric(item) = 1)
            Order By [country]
            For XML Path('')) As [CountriesByName]
    From [Users] As U) As [Results]

【讨论】:

  • 正确的解决方案是规范化数据,因为如果你不这样做,你必须编写像这样的混乱代码(而且我们不是都至少写过一次这个函数吗?)然而,看起来可以安全地假设修改表格不是一种选择——它从来都不是,是吗? -- 所以这可能是这个问题的正确答案。
【解决方案2】:

尝试Common Table Expression 查询。 Simple-Talk 有一个非常好的walkthrough,它解释了 SQL 连接的不同方法并给出了使用 CTE 的示例(查找 WITH 语句)。

【讨论】:

    【解决方案3】:

    如果您将数据规范化为三个表,则以下内容适用于您想要执行的操作。这是使用我自己的架构,因为我不得不敲一些表来测试它)。

    Select
    UserId, UserName,
    (
        Select
            CountryName + ',' 
        From
            Country As C
        Inner Join
            UserCountry As UC
        On  C.CountryId = UC.CountryId
        Where
            UC.UserId = [User].UserId
        ORDER BY
            CountryName
        FOR XML PATH('')
    ) As Countries
    

    从 [用户];

    【讨论】:

    • 查看我对 Ryans 评论的评论 -> 我无法更改数据库方案 :(
    【解决方案4】:

    首先,我将创建一个存储过程,它采用国家 ID 字符串并使用动态 sql 返回国家名称列表。

    create proc dbo.getCoutries(@ids nvarchar(200))
    as
    begin
    declare @sql  nvarchar(200)
    set @sql = 'select @countries = @countries  + Country+
        '', '' from Countries where ID in ('+@ids+')'
    declare @countries nvarchar(200)
    set @countries = ''
    DECLARE @ParmDefinition nvarchar(500);
    SET @ParmDefinition = N'@countries nvarchar(200) OUTPUT';
    
    
    EXECUTE sp_executesql @sql, 
       @ParmDefinition, 
       @countries=@countries OUTPUT;
    
    select substring(@countries,1,len(@countries)-1) Countries
    
    end
    
    go
    

    所以现在 exec getCoutries '2, 5' 返回“德国、加拿大”。 完成后,我们现在需要运行下面的代码 返回如下表格:

    Name     Countries
    User1   Australia, Germany, USA
    User2   Germany, Canada
    
    
    
    --The variable we will use to loop thru the user ids
    declare @userId int
    set @userId = 0
    
    -- hold the string of country ids
    declare @countryIds varchar(30)
    
    -- a temp table that we will populate
    -- it will have the users ID and then the 
    -- string of countries (i.e "Germany, Canada")
    declare @results as 
    table 
    (userId int, 
    countries varchar(200))
    
    
    --Loop thru each row in the Users table.
    while 1=1
    begin
    select @userId=min(ID)
    from Users
    where ID > @userId
    
    if @userId is null
        break
    
    --get the string of country ids for this user
    select @countryIds=Countries
    from Users
    where ID = @userId
    
    --use our stored proc to bring back the string of names
    insert into @results
    (countries)
    exec getCoutries @countryIds
    
    --update the userId for our new row
    update @results
    set UserId = @UserId
    where UserId is null
    
    
    end
    
    -- return the user and his list of countries
    select u.Name, r.Countries
    from Users u
    inner join @results r
    on u.ID = r.UserId
    
    GO
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-07-20
      • 2012-04-02
      • 2022-06-16
      • 2012-02-26
      • 1970-01-01
      • 2011-03-29
      • 2021-09-01
      • 1970-01-01
      相关资源
      最近更新 更多