【问题标题】:What is the best way to join between two table which have coma seperated columns在具有逗号分隔列的两个表之间连接的最佳方法是什么
【发布时间】:2015-07-02 09:23:39
【问题描述】:

表1

ID        Name              Tags 
----------------------------------
1        Customer1         Tag1,Tag5,Tag4 
2        Customer2         Tag2,Tag6,Tag4,Tag11 
3        Customer5         Tag6,Tag5,Tag10 

和表2

ID     Name             Tags 
----------------------------------
1    Product1     Tag1,Tag10,Tag6 
2    Product2     Tag2,Tag1,Tag5 
3    Product5   Tag1,Tag2,Tag3 

将 Table1 和 Table2 与标签列连接的最佳方法是什么?

对于表 1 中标签列上的每个逗号分隔标签,它应该查看表 2 上逗号分隔的标签列

注意:表格不是全文索引。

【问题讨论】:

  • 你的问题让我想到了一个 HTML 解决方案 (jquery),但标签 sql 让我不这么认为
  • 列中的 CVS 经常使用,但这是非常糟糕的做法。重新设计数据库很好。但是,至少您可以用逗号 (",Tag1,Tag2,Tag3,") 将第一个和最后一个值括起来,以便能够使用Tags LIKE '%,Tag1,%'. Otherwise you may scan LIKE '%Tag1%'` 进行扫描并找到“Tag19”。
  • 您也许可以使用来自stackoverflow.com/q/5493510/121309 的解决方案来创建中间表并对其进行查询。不过不要指望任何表现。

标签: sql sql-server


【解决方案1】:

最好的方法是不在列中使用逗号分隔值。只需使用标准化数据,这样查询就不会遇到问题 - 每列应该只有一个值。

没有这个,真的没有办法使用任何索引。即使是全文索引的行为也与您可能认为的完全不同,而且它们本质上使用起来很笨拙——它们是为搜索 text 而设计的,而不是有意义的数据。最后,你不会比类似的东西更好

where (Col like 'txt,%' or Col like '%,txt' or Col like '%,txt,%')

使用xml 列可能是另一种选择,尽管它仍然有点傻。不过,它至少允许您将这些值视为一个集合。

【讨论】:

  • 谢谢你的回答。您对我的解决方案有何看法... 步骤 1:用“,”逗号拆分 Table1 并制作一个垂直表格。例如#dt with distinct tags=> Tag1 Tag2 Tag3 Step 2 : Join Table2 with #dt
  • @memettayanc 关于执行此操作的最简单和最合适的方法 - 无需将标签存储在单个 varchar 列中,只需使用表格即可。在查询数据时修复它几乎可以保证性能不佳 - 真的没有办法使用索引。如果您只是在运行查询时使用临时表执行此操作,您会得到比上面的 like 示例更糟糕的情况。
【解决方案2】:

我认为永远不会有简单有效的解决方案来解决这个问题。正如 Luaan 所指出的,像这样存储数据是一个非常糟糕的主意:当您将应该是单独的数据单元压缩到单个单元格中时,您将失去 SQL 的大部分功能。

但是您可以通过创建两个用户定义的函数来管理这个问题。首先,使用this brilliant recursive technique 根据您的分隔符将字符串拆分为单独的行:

CREATE FUNCTION dbo.TestSplit (@sep char(1), @s varchar(512))
RETURNS table
AS
RETURN (
    WITH Pieces(pn, start, stop) AS (
      SELECT 1, 1, CHARINDEX(@sep, @s)
      UNION ALL
      SELECT pn + 1, stop + 1, CHARINDEX(@sep, @s, stop + 1)
      FROM Pieces
      WHERE stop > 0
    )
    SELECT pn AS SplitIndex,
      SUBSTRING(@s, start, CASE WHEN stop > 0 THEN stop-start ELSE 512 END) AS SplitPart
    FROM Pieces
  )

然后,创建一个接受两个字符串并计算匹配项的函数:

CREATE FUNCTION dbo.MatchTags (@a varchar(512), @b varchar(512))
RETURNS INT
AS
BEGIN
RETURN 
(SELECT COUNT(*)
FROM dbo.TestSplit(',', @a) a 
INNER JOIN dbo.TestSplit(',', @b) b 
    ON a.SplitPart = b.SplitPart)
END

就是这样,这是一个带有表变量的测试卷:

DECLARE @A TABLE (Name VARCHAR(20), Tags VARCHAR(100))
DECLARE @B TABLE (Name VARCHAR(20), Tags VARCHAR(100))

INSERT INTO @A ( Name, Tags )
VALUES 
( 'Customer1','Tag1,Tag5,Tag4'),
( 'Customer2','Tag2,Tag6,Tag4,Tag11'),
( 'Customer5','Tag6,Tag5,Tag10')

INSERT INTO @B ( Name, Tags )
VALUES 
( 'Product1','Tag1,Tag10,Tag6'),
( 'Product2','Tag2,Tag1,Tag5'),
( 'Product5','Tag1,Tag2,Tag3')

SELECT * FROM @A a
INNER JOIN @B b ON dbo.MatchTags(a.Tags, b.Tags) > 0

【讨论】:

  • 感谢您的回答,您的脚本运行良好,但速度太慢。表行数= 2541 表2行数= 885 选择==> 132 秒i57.tinypic.com/inxzqt.png
【解决方案3】:

我制定了如下解决方案:

CREATE TABLE [dbo].[Table1](
Id      int not null,
Name    nvarchar(250) not null,
Tag     nvarchar(250)  null,
) ON [PRIMARY]
GO

CREATE TABLE [dbo].[Table2](
Id      int not null,
Name    nvarchar(250) not null,
Tag     nvarchar(250)  null,
) ON [PRIMARY]
GO

获取Table1的样本数据,它将插入28000条记录

INSERT INTO Table1
SELECT CustomerID,CompanyName, (FirstName + ',' + LastName)
FROM AdventureWorks.SalesLT.Customer 
GO 3    

Table2 的示例数据.. 我需要 Table2 的相同标签

declare @tag1 nvarchar(50) = 'Donna,Carreras'
declare @tag2 nvarchar(50) = 'Johnny,Caprio'

获取Table2的样本数据,它将插入9735条记录

 INSERT INTO Table2
    SELECT ProductID,Name, (case when(right(ProductID,1)>=5) then  @tag1 else @tag2 end)
    FROM AdventureWorks.SalesLT.Product 
    GO 3 

我的解决方案

create TABLE #dt (
        Id int IDENTITY(1,1) PRIMARY KEY,
        Tag nvarchar(250) NOT NULL
    );

我已经创建了临时表,我将在 Table1 中填充 Distinct 标记

insert into #dt(Tag)
SELECT distinct Tag
FROM      Table1

现在我需要为标签垂直表格

  create TABLE #Tags (  Tag nvarchar(250) NOT NULL );

现在我用 While 填充#Tags 表,您可以使用 Cursor 但 while 更快

declare @Rows int = 1
declare @Tag nvarchar(1024)
declare @Id int = 0

WHILE @Rows>0
BEGIN
    Select Top 1 @Tag=Tag,@Id=Id from #dt where Id>@Id
    set @Rows =@@RowCount
    if @Rows>0
    begin
        insert into #Tags(Tag)   SELECT Data FROM dbo.StringToTable(@Tag, ',')
    end
END

最后一步:使用#Tags 加入 Table2

select distinct t.*
from Table2 t  
    inner join  #Tags on (',' + t.Tag + ',') like ('%,' + #Tags.Tag + ',%')

Table rowcount=28000 Table2 rowcount=9735 选择小于2秒

【讨论】:

    【解决方案4】:

    我将这种解决方案用于树木路径。首先在字符串的开头和结尾放置一个逗号。比你可以打电话

    Where col1 like '%,' || col2 || ',%'
    

    一些数据库也为类似的列索引(postgres 部分执行),因此也很有效。我不知道sqlserver。

    【讨论】:

    • MS SQL 使用+ 进行字符串合并。它确实使用like 的索引,但 当它是something% - 这就是索引的排序方式。我很确定 postgres 会做类似的事情。哦,当然,它可能会选择扫描索引——我说的是搜索。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-10-19
    • 1970-01-01
    • 1970-01-01
    • 2017-02-08
    • 2018-08-20
    • 2013-10-06
    相关资源
    最近更新 更多