连接表包含逗号分隔的值

时间:2018-10-03 06:09:36

标签: sql sql-server tsql

我有三个Excel工作表,我将它们推入SQL Server中的表中,我需要加入这些表。但是,我相信-正如我已经尝试过的-普通加入将不起作用。我有编程背景,但是对SQL没有那么了解。

Table1

ID  Data_column reference_number
1   some data   1528,ss-456
2   some data   9523
3   some data   ss-952
4   some data   null

Table2 

ID      Data_column
ss-456  some data
ss-952  some data


Table3 

ID      Data_column
1528    some data
9523    some data

在下面的情况下,我将如何在两个表上加入此原始数据。

Table1

ID  Data_column reference_number
1   some data   1528,ss-456

6 个答案:

答案 0 :(得分:2)

declare @t1 as table(
     id int
    ,data_column varchar(20)
    ,reference_number varchar(20)
)

declare @t2 as table(
     id varchar(20)
    ,data_column varchar(20)
)

declare @t3 as table(
     id varchar(20)
    ,data_column varchar(20)
)

insert into @t1 values(1,'some data','1528,ss-456'),(2,'some data','9523'),(3,'some data','ss-952'),(4,'some data',null);

insert into @t2 values('ss-456','some data'),('ss-952','some data');

insert into @t3 values(1528,'some data'),(9523,'some data');

快速解决方案

select * from @t1 t1
left outer join @t2 t2 on t1.reference_number like '%'+t2.id or t1.reference_number like t2.id+'%'
left outer join @t3 t3 on t1.reference_number like '%'+t3.id or t1.reference_number like t3.id+'%'

结果(左联接):

id  data_column reference_number    id      data_column id  data_column
1   some data   1528,ss-456         ss-456  some data   1528    some data
2   some data   9523                NULL    NULL        9523    some data
3   some data   ss-952              ss-952  some data   NULL    NULL
4   some data   NULL                NULL    NULL        NULL    NULL

您可以将“左外部连接”更改为“内部连接”以实现完全匹配。

答案 1 :(得分:1)

笨拙的设计,笨拙的解决方案:

SELECT *
FROM Table1
INNER JOIN Table2 ON ',' + Table1.reference_number + ',' LIKE '%,' + Table2.ID + ',%'
INNER JOIN Table3 ON ',' + Table1.reference_number + ',' LIKE '%,' + Table3.ID + ',%' 

必须 附加前导和尾随逗号,以确保例如1528,ss-456asdf%ss-456%不匹配。

答案 2 :(得分:0)

我在这里看到两个问题。首先是表2和3中ID的不一致类型和表1中所引用密钥的聚合。这是解决这两个问题的示例。为了拆分REFERENCE_NUMBER列,我使用了STRING_SPLIT函数。

更新: 我添加了适用于SQL Server 2012的解决方案。

我假设您希望将表1中的数据与2或3连接起来,具体取决于此数据的存在。这只是我的想法,您想要达到的目标。

-- data preparing
declare @t1 as table(
     id int
    ,data_column varchar(20)
    ,reference_number varchar(20)
)

declare @t2 as table(
     id varchar(20)
    ,data_column varchar(20)
)

declare @t3 as table(
     id int
    ,data_column varchar(20)
)

insert into @t1 values(1,'some data','1528,ss-456'),(2,'some data','9523'),(3,'some data','ss-952'),(4,'some data',null);

insert into @t2 values('ss-456','some data'),('ss-952','some data');

insert into @t3 values(1528,'some data'),(9523,'some data');

-- Solution example version >= 2016
with base as (
select t1.id,t1.data_column,f1.value from @t1 t1 outer apply string_split(t1.reference_number,',') f1)
select b.id,b.data_column,b.value,t2.data_column from base b join @t2 t2 on b.value = t2.id
union all
select b.id,b.data_column,b.value,t3.data_column from base b join @t3 t3 on try_cast(b.value as int ) = t3.id
union all
select b.id,b.data_column,b.value,null from base b where b.value is null;

-- Solution for SQL Version < 2016
with base as (
select t1.id,t1.data_column,f1.value from @t1 t1 outer apply( 
    SELECT Split.a.value('.', 'NVARCHAR(MAX)') value
FROM
(
    SELECT CAST('<X>'+REPLACE(t1.reference_number, ',', '</X><X>')+'</X>' AS XML) AS String
) AS A
CROSS APPLY String.nodes('/X') AS Split(a)
) f1)
select b.id,b.data_column,b.value,t2.data_column from base b join @t2 t2 on b.value = t2.id
union all
select b.id,b.data_column,b.value,t3.data_column from base b join @t3 t3 on try_cast(b.value as int ) = t3.id
union all
select b.id,b.data_column,b.value,null from base b where b.value is null;

答案 3 :(得分:0)

您将需要一个函数来将逗号分隔的字符串分成几行。如果您无权访问内置string_split() function(从mssql 2017开始,兼容性为130),则可以选择here

CREATE TABLE table1(
   ID               INTEGER  NOT NULL PRIMARY KEY 
  ,Data_column      VARCHAR(10) NOT NULL
  ,reference_number VARCHAR(11)
);
INSERT INTO table1(ID,Data_column,reference_number) VALUES
  (1,'t1somedata','1528,ss-456')
, (2,'t1somedata','9523')
, (3,'t1somedata','ss-952')
, (4,'t1somedata',NULL);
CREATE TABLE table2(
   ID           VARCHAR(6) NOT NULL PRIMARY KEY
  ,Data_column VARCHAR(10) NOT NULL
);
INSERT INTO table2(ID,Data_column) VALUES
 ('ss-456','t2somedata'),
 ('ss-952','t2somedata');
CREATE TABLE table3(
   ID           VARCHAR(6) NOT NULL PRIMARY KEY
  ,Data_column VARCHAR(10) NOT NULL
);
INSERT INTO table3(ID,Data_column) VALUES 
('1528','t3somedata'),
('9523','t3somedata');

我已经使用了这个splistring函数,但是您可以使用几乎所有免费版本中的任何一个。

CREATE FUNCTION dbo.SplitStrings_Moden
(
   @List NVARCHAR(MAX),
   @Delimiter NVARCHAR(255)
)
RETURNS TABLE
WITH SCHEMABINDING AS
RETURN
  WITH E1(N)        AS ( SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 
                         UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 
                         UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1),
       E2(N)        AS (SELECT 1 FROM E1 a, E1 b),
       E4(N)        AS (SELECT 1 FROM E2 a, E2 b),
       E42(N)       AS (SELECT 1 FROM E4 a, E2 b),
       cteTally(N)  AS (SELECT 0 UNION ALL SELECT TOP (DATALENGTH(ISNULL(@List,1))) 
                         ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E42),
       cteStart(N1) AS (SELECT t.N+1 FROM cteTally t
                         WHERE (SUBSTRING(@List,t.N,1) = @Delimiter OR t.N = 0))
  SELECT Item = SUBSTRING(@List, s.N1, ISNULL(NULLIF(CHARINDEX(@Delimiter,@List,s.N1),0)-s.N1,8000))
    FROM cteStart s;

使用splitstring函数,数据看起来像这样:

select *
from table1
cross apply SplitStrings_Moden(reference_number,',')
ID | Data_column | reference_number | Item  
-: | :---------- | :--------------- | :-----
 1 | t1somedata  | 1528,ss-456      | 1528  
 1 | t1somedata  | 1528,ss-456      | ss-456
 2 | t1somedata  | 9523             | 9523  
 3 | t1somedata  | ss-952           | ss-952
 4 | t1somedata  | null             | null  

现在加入其他表:

select
*
from (
select *
from table1
cross apply SplitStrings_Moden(reference_number,',')
) t1
left join table2 on t1.item = table2.id
left join table3 on t1.item = table3.id
where t1.item is not null

GO
ID | Data_column | reference_number | Item   | ID     | Data_column | ID   | Data_column
-: | :---------- | :--------------- | :----- | :----- | :---------- | :--- | :----------
 1 | t1somedata  | 1528,ss-456      | 1528   | null   | null        | 1528 | t3somedata 
 1 | t1somedata  | 1528,ss-456      | ss-456 | ss-456 | t2somedata  | null | null       
 2 | t1somedata  | 9523             | 9523   | null   | null        | 9523 | t3somedata 
 3 | t1somedata  | ss-952           | ss-952 | ss-952 | t2somedata  | null | null       

db <>提琴here

答案 4 :(得分:0)

请尝试:如果您的reference_number是固定的,并且始终只能存储最多2个ID,那么您可以采用以下方法

SELECT *
FROM(
    SELECT ID, 
        data_column, 
        CASE WHEN PATINDEX ( '%,%', reference_number) > 0 THEN 
            SUBSTRING(reference_number, PATINDEX ( '%,%', reference_number)+1, LEN(reference_number)) 
        ELSE reference_number END AS ref_col
    FROM @table1
    UNION
    SELECT ID, 
        data_column, 
        CASE WHEN PATINDEX ( '%,%', reference_number) > 0 THEN 
            SUBSTRING(reference_number, 0, PATINDEX ( '%,%', reference_number)) 
        END
    FROM @table1) t1
LEFT JOIN @table2 t2 ON t2.id = t1.ref_col
LEFT JOIN @table3 t3 ON t3.id = t1.ref_col
WHERE t1.ref_col IS NOT NULL

输出:

ID  data_column ref_col ID      Data_column ID      Data_column
1   some data   1528    NULL    NULL        1528    some data
1   some data   ss-456  ss-456  some data   NULL    NULL
2   some data   9523    NULL    NULL        9523    some data
3   some data   ss-952  ss-952  some data   NULL    NULL
4   some data   null    NULL    NULL        NULL    NULL    

答案 5 :(得分:0)

您可以使用Substring上的charIndexreference_number函数来实现并获得期望的结果。

我赞成使用“ is_oz”作为答案,因为我使用了现成的架构来为您测试和建立查询。

以下是我在several tries i made here之后建立的最终查询:

select * from abc
left join abc2 on abc2.id = case when charindex(',',abc.reference_number) > 0
                             then substring(abc.reference_number
                                       ,charindex(',',abc.reference_number)+1
                                       ,len(abc.reference_number)-(charindex(',',abc.reference_number)-1)
                                      ) 
                             else abc.reference_number
                             end
left join abc3 on abc3.id = case when charindex(',',abc.reference_number) > 0
                             then substring(abc.reference_number
                                       ,0
                                       ,(charindex(',',abc.reference_number))
                                      ) 
                             else abc.reference_number
                             end

据我所知,根据您的要求,它正在从其他2个表中返回所有匹配的行,但是我仍然希望这能够满足您在问题中寻求的所有要求。 :)