SQL выберите объединенную строку для набора идентификаторов - PullRequest
0 голосов
/ 15 июля 2009

Мне приходится иметь дело с базой данных 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

Теперь, то, что я ищу, это оператор выбора, который даст мне такой результат:

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

Я бы предпочел решение, которое не будет зависеть от специального синтаксиса MSSQL, а не от чего-то особенного, но я не могу использовать некоторую магию LINQ: (

Ответы [ 4 ]

6 голосов
/ 15 июля 2009

Во-первых, вам нужно разделить эту строку. Вот работающая функция разделения:

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]
1 голос
/ 15 июля 2009

Попробуйте запрос Common Table Expression . Simple-Talk имеет очень приятное пошаговое руководство , которое объясняет различные подходы к распространению SQL и дает примеры использования CTE (ищите операторы WITH).

0 голосов
/ 15 июля 2009

Сначала я бы сделал хранимый процесс, который принимает строку идентификаторов стран и возвращает список названий стран, используя динамический 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
0 голосов
/ 15 июля 2009

Если вы нормализуете свои данные в три таблицы, то следующее работает для того, что вы хотите сделать. Я использую мою собственную схему, так как мне пришлось разбить некоторые таблицы, чтобы протестировать ее).

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

С [Пользователь];

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...