2

我有一个产品值如下表:

  1. 苹果 iphone

  2. iphone 苹果

  3. 三星手机

  4. 手机三星

我想从表中删除那些完全相反的产品(因为我认为它们是重复的),这样我的表就只有 2 条记录,而不是 4 条记录

  1. 苹果 iphone

  2. 三星手机

我知道 SQL Server 中有 REVERSE 函数,但它会反转整个字符串,而不是我想要的。

我非常感谢任何建议/想法。

4

5 回答 5

5

假设您的字典不包含任何 XML 实体(例如>or <),并且为表中的每个单词组合手动创建一堆UPDATE语句是不切实际的(如果可行,那么简化您的生活,停止阅读本文回答,并使用贾斯汀的回答),您可以创建一个这样的函数:

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 是个问题,还有其他更复杂的替代方案,例如 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
于 2013-08-23T18:31:52.240 回答
5

在我看来,你把这件事复杂化了,一个简单的更新语句就可以了:

UPDATE table SET productname = 'apple iphone' WHERE productname = 'iphone apple'
于 2013-08-23T18:02:04.887 回答
3

我不知道如何在 SQL 中执行此操作,但是在您与 SQL 交互的语言中,您可以执行此操作:

您可以对每一行进行标记,以便您拥有一个单词数组,以便“iphone apple”变为 {“iphone”,“apple”},然后您可以使用通用交换语句切换元素的顺序,使其变为 { "apple","iphone"} 然后你可以把它转回一个字符串来制作 "apple iphone"

虽然我上面描述的过程并不是那么难,但找出哪些是彼此重复的(知道要翻转哪些)可能是一个更难的问题

于 2013-08-23T18:06:50.947 回答
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

SQLFiddle

于 2013-08-23T18:19:09.263 回答
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,但你应该设法把它弄平:) 祝你好运

于 2013-08-23T18:08:51.570 回答