在两个具有昏迷分隔列的表之间进行连接的最佳方法是什么

时间:2015-07-02 09:23:40

标签: sql sql-server

表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与Tags列连接的最佳方法是什么?

表1中的标签栏上的每个昏迷分离标签应该看表2中昏迷分离的标签栏

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

enter image description here

4 个答案:

答案 0 :(得分:2)

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

没有这个,真的没有办法使用任何指数。即使是全文索引的行为与您可能的行为完全不同,它们本质上也很笨拙 - 它们的设计目的是搜索 text ,而不是有意义的数据。最后,你不会比

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

使用xml列可能是另一种选择,尽管它仍然有点愚蠢。但是,它允许您至少将值视为集合。

答案 1 :(得分:1)

我认为不会有一个简单而有效的解决方案。正如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

答案 2 :(得分:1)

我开发了如下解决方案:

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
    );

我已经创建了临时表,我将在表1中填写 Distinct 标签

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

现在我需要标签的垂直表

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

现在我用填充#Tags表格,你可以使用Cursor,但速度更快

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 + ',%')

表rowcount = 28000 Table2 rowcount = 9735 select小于2秒

答案 3 :(得分:0)

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

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

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