我有一张产品值如下表:
apple iphone
iphone apple
三星手机
电话三星
我想从表中删除那些完全相反的产品(因为我认为它们是重复的),这样我的表只有2条记录而不是4条记录
apple iphone
三星手机
我知道SQL Server中有REVERSE函数,但它会反转整个字符串,而不是我正在寻找的。
我非常感谢任何建议/想法。
答案 0 :(得分:5)
在我看来,你太复杂了,一个简单的更新声明就可以了:
UPDATE table SET productname = 'apple iphone' WHERE productname = 'iphone apple'
答案 1 :(得分:5)
假设您的词典不包含任何XML实体(例如>
或<
),并且为每个组合手动创建一堆UPDATE
语句是不切实际的。在你的表格中的单词(如果它是实用的,然后简化你的生活,停止阅读这个答案,并使用Justin's answer),你可以创建一个这样的函数:
CREATE FUNCTION dbo.SplitSafeStrings
(
@List NVARCHAR(MAX),
@Delimiter NVARCHAR(255)
)
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN
( SELECT Item = LTRIM(RTRIM(y.i.value('(./text())[1]', 'nvarchar(4000)')))
FROM ( SELECT x = CONVERT(XML, '<i>'
+ REPLACE(@List, @Delimiter, '</i><i>') + '</i>').query('.')
) AS a CROSS APPLY x.nodes('i') AS y(i));
GO
(如果XML是个问题,there are other, more complex alternatives,例如CLR。)
然后你可以这样做:
DECLARE @x TABLE(id INT IDENTITY(1,1), s VARCHAR(64));
INSERT @x(s) VALUES
('apple iphone'),
('iphone Apple'),
('iphone samsung hoochie blat'),
('samsung hoochie blat iphone');
;WITH cte1 AS
(
SELECT id, Item FROM @x AS x
CROSS APPLY dbo.SplitSafeStrings(LOWER(x.s), ' ') AS y
),
cte2(id,words) AS
(
SELECT DISTINCT id, STUFF((SELECT ',' + orig.Item
FROM cte1 AS orig
WHERE orig.id = cte1.id
ORDER BY orig.Item
FOR XML PATH(''), TYPE).value('.[1]','nvarchar(max)'),1,1,'')
FROM cte1
),
cte3 AS
(
SELECT id, words, rn = ROW_NUMBER() OVER (PARTITION BY words ORDER BY id)
FROM cte2
)
SELECT id, words, rn FROM cte3
-- WHERE rn = 1 -- rows to keep
-- WHERE rn > 1 -- rows to delete
;
所以你可以在三个CTE之后,而不是上面的SELECT
,说:
DELETE t FROM @x AS t
INNER JOIN cte3 ON cte3.id = t.id
WHERE cte3.rn > 1;
@x
应该留下什么?
SELECT id, s FROM @x;
结果:
id s
-- ---------------------------
1 apple iphone
3 iphone samsung hoochie blat
答案 2 :(得分:3)
我不知道如何在SQL中执行此操作,但是在与SQL接口的语言中,您可以这样做:
您可以对每一行进行标记,以便您拥有一个单词数组,以便“iphone apple”变为{“iphone”,“apple”}然后您可以使用公共交换语句切换元素的顺序,以便它变成{“apple”,“iphone”}然后你可以把它变成一个字符串来制作“apple iphone”
虽然我上面描述的过程并不是那么难做,但找出哪些是彼此重复的(知道哪些要翻转)可能是一个更难的问题
答案 3 :(得分:2)
根据你提供的数据示例,你可以尝试这样的事情:
如果productname的“正确”格式为<brand> <product_type>
,您只需删除productname not like '<brand>%'
的所有产品即可。
如果上述情况无济于事 - 是否有任何产品命名规则?
由于无法应用上述构思,请创建Split
函数:
CREATE FUNCTION [dbo].[Split]
(
@String NVARCHAR(4000),
@Delimiter NCHAR(1)
)
RETURNS TABLE
AS
RETURN
(
WITH Split(stpos,endpos)
AS(
SELECT 0 AS stpos, CHARINDEX(@Delimiter,@String) AS endpos
UNION ALL
SELECT endpos+1, CHARINDEX(@Delimiter,@String,endpos+1)
FROM Split
WHERE endpos > 0
)
SELECT 'Id' = ROW_NUMBER() OVER (ORDER BY (SELECT 1)),
'Data' = SUBSTRING(@String,stpos,COALESCE(NULLIF(endpos,0),LEN(@String)+1)- stpos)
FROM Split
)
在查询中使用它:
select
(SELECT (', ' + Data)
FROM Split(t.textVal, ' ')
order by [Data]
FOR XML PATH( '' )
)
from
test t
这将为您提供带有排序单词的产品名称。有了它,您可以轻松找到重复项。 第二个查询是粗糙的边缘,因为我要去afk,但你应该设法平滑它:) 祝你好运
答案 4 :(得分:2)
这是一个由空格分隔的两个或多个单词的解决方案。基本上这个想法是使用递归CTE按空格分割,然后使用xml将名称重新排序。然后,您可以按新名称列进行分组,以获取重复数据删除列表:
with split as (
select id,
convert(varchar(max), left(name, charindex(' ', name + ' ') - 1)) word,
stuff(name, 1, charindex(' ', name + ' '), '') name
from products
union all
select id,
convert(varchar(max), left(name, charindex(' ', name + ' ') - 1)) word,
stuff(name, 1, charindex(' ', name + ' '), '') name
from split where name > ''
),
hom as (
select id,
(select word + ' '
from split where id=o.id
order by word for xml path('')) name
from split o
)
select name, min(id) id from hom group by name