【问题标题】:How to query a table with multiple values in one column with join如何使用连接查询一列中具有多个值的表
【发布时间】:2017-08-25 23:47:42
【问题描述】:

表 1:

A       B       C
Test    1       This
Test1   1;4     That
Test2   7       What
Test3   6;2     Which
Test4   1;2;7   Where

表 2:

X       Z
1       Sun
2       Mon
3       Tue
4       Wed
5       Thu
6       Fri
7       Sat

Sql:

Select
    t1.A,
    t2.Z
from
    [dbo].[Table 1] t1
    inner join [dbo].[Table2] t2
    on t1.B = t2.X

它仅适用于在 B 列中只有 1 个条目但在 2 个或更多条目处失败的行。

我怎样才能修改 Sql 以便它给我这样的结果:

A       Z
Test    Sun
Test1   Sun;Wed
Test2   Sat
Test3   Fri;Mon
Test4   Sun;Mon;Sat

【问题讨论】:

  • 您使用什么数据库引擎?其次,为什么将多个值存储在单个列中?那是糟糕的桌子设计。能改吗?
  • 对不起...让我更新我的问题。而且我知道,不幸的是,这是给我们的设计。
  • 这不适合 RDBMS。从Table 2 检索数据,并使用更适合该问题的语言(Java、C#、C++)在内存中执行搜索和替换。
  • 修复您的数据!不要将 numeric id 列表存储为分隔的 string 列表。这只是在关系数据库中存储数据的错误方式。
  • 如果您无法修复表格设计,您可能需要调查字符串拆分器。这里有一些很好的选择。 sqlperformance.com/2012/07/t-sql-queries/split-strings

标签: sql sql-server tsql sql-server-2012 inner-join


【解决方案1】:

字符串和 XML 的乐趣,这里有一个小(按比例缩小)技术来标记数据。

创建一些示例数据

Declare @Table1 table (A varchar(100),B varchar(100), C varchar(100))
Insert Into @Table1 values
('Test','1','This'),('Test1','1;4','That'),('Test2','7','What'),('Test3','6;2','Which'),('Test4','1;2;7','Where')

Declare @Table2 table (X int,Z varchar(100))
Insert Into @Table2 values
(1,'Sun'),(2,'Mon'),(3,'Tue'),(4,'Wed'),(5,'Thu'),(6,'Fri'),(7,'Sat')

SQL

Declare @XML xml,@Str varchar(max) = (Select a,z='['+replace(b,';','];[')+']' From  @Table1 For XML Raw)
Select @Str = Replace(@Str,'['+cast(X as varchar(25))+']',Z) From  @Table2
Select @XML = @Str

Select a = r.value('@a','varchar(100)')
      ,z = r.value('@z','varchar(100)')
From  @XML.nodes('/row') as A(r)

退货

a       z
Test    Sun
Test1   Sun;Wed
Test2   Sat
Test3   Fri;Mon
Test4   Sun;Mon;Sat

【讨论】:

    【解决方案2】:

    您确实不应该在同一列中存储多个值,这只会在您实际必须对这些值执行某些操作时导致性能下降。


    使用 Jeff Moden 的 CSV 拆分器表值函数和 stuff() with select ... for xml path ('') method of string concatenation。 :

    select
        t1.a
      , z = stuff((
          select ';'+t2.Z
          from t1 i
            cross apply dbo.delimitedsplit8K(i.b,';') s
            inner join t2 
              on s.Item = t2.x
          where i.a = t1.a
          order by s.ItemNumber
          for xml path(''),type).value('(./text())[1]','nvarchar(max)')
        ,1,1,'')
    from t1
    

    rextester 演示

    返回:http://rextester.com/HNNP95095

    +-------+-------------+
    |   a   |      z      |
    +-------+-------------+
    | Test  | Sun         |
    | Test1 | Sun;Wed     |
    | Test2 | Sat         |
    | Test3 | Fri;Mon     |
    | Test4 | Sun;Mon;Sat |
    +-------+-------------+
    

    拆分字符串参考:


    Jeff Moden 用于演示的函数:
    create function [dbo].[delimitedsplit8K] (
          @pstring varchar(8000)
        , @pdelimiter char(1)
      )
    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)
      , ctetally(N) as (
        select top (isnull(datalength(@pstring),0)) 
          row_number() over (order by (select null)) from e4
      )
      , ctestart(N1) as (
        select 1 union all
        select t.N+1 from ctetally t where substring(@pstring,t.N,1) = @pdelimiter
      )
      , ctelen(N1,L1) as (
        select s.N1,
          isnull(nullif(charindex(@pdelimiter,@pstring,s.N1),0)-s.N1,8000)
        from ctestart s
      )
     select itemnumber = row_number() over(order by l.N1)
          , item       = substring(@pstring, l.N1, l.L1)
       from ctelen l
    ;
    go
    

    【讨论】:

      猜你喜欢
      • 2018-05-30
      • 1970-01-01
      • 2014-03-09
      • 1970-01-01
      • 1970-01-01
      • 2019-02-14
      • 2016-12-19
      • 2017-10-23
      • 1970-01-01
      相关资源
      最近更新 更多