表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中昏迷分离的标签栏
注意:表格不是全文索引。
答案 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
加入Table2select 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。