Inverser des strings dans SQL Server

J'ai un tableau avec des valeurs de produit comme ci-dessous:

  1. iphone Apple

  2. pomme iphone

  3. téléphone Samsung

  4. téléphone Samsung

Je veux supprimer ces produits de la table qui sont exactement inverses (comme je les considère comme des duplicates), de sorte qu'au lieu de 4 loggings, ma table a juste 2 loggings

  1. iphone Apple

  2. téléphone Samsung

Je comprends qu'il existe une fonction REVERSE dans SQL Server, mais elle va inverser la string entière, et ce n'est pas ce que je cherche.

J'apprécierais grandement vos suggestions / idées.

En supposant que votre dictionary n'inclue aucune entité XML (par exemple > ou < ), et qu'il n'est pas pratique de créer manuellement un tas d'instructions UPDATE pour chaque combinaison de mots dans votre tableau (si cela est pratique, alors simplifiez-vous la vie, arrêtez de lire cette réponse, et utilisez la réponse de Justin ), vous pouvez créer une fonction comme celle-ci:

 CREATE FUNCTION dbo.SplitSafeSsortingngs ( @List NVARCHAR(MAX), @Delimiter NVARCHAR(255) ) RETURNS TABLE WITH SCHEMABINDING AS RETURN ( SELECT Item = LTRIM(RTRIM(yivalue('(./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 

(Si XML est un problème, il existe d'autres alternatives plus complexes , telles que CLR.)

Alors vous pouvez faire ceci:

 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.SplitSafeSsortingngs(LOWER(xs), ' ') 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 ; 

Donc, vous pourriez, après les trois CTE, au lieu du dernier SELECT ci-dessus, dire:

 DELETE t FROM @x AS t INNER JOIN cte3 ON cte3.id = t.id WHERE cte3.rn > 1; 

Et que devrait-il restr dans @x ?

 SELECT id, s FROM @x; 

Résultats:

 id s -- --------------------------- 1 apple iphone 3 iphone samsung hoochie blat 

Il me semble que vous compliquez cela trop, une simple mise à jour fonctionnerait:

 UPDATE table SET productname = 'apple iphone' WHERE productname = 'iphone apple' 

Je ne sais pas comment faire cela en SQL, mais dans un langage où vous vous connectez avec SQL, vous pouvez faire ceci:

Vous pouvez marquer chaque ligne de sorte que vous ayez un tableau de mots, de sorte que "iphone apple" devienne {"iphone", "apple"} et que vous puissiez ensuite changer l'ordre des éléments en utilisant une instruction swap commune pour qu'elle devienne { "apple", "iphone"} et ensuite vous pouvez le returnner dans une string pour faire "Apple iPhone"

Bien que le process que je décris ci-dessus ne soit pas si difficile à faire, découvrir quels sont les duplicates les uns des autres (savoir lesquels returnner) pourrait être un problème plus difficile

Sur la base des exemples de données que vous avez fournis, vous pouvez essayer quelque chose comme ceci:

Dans le cas où le format "correct" pour productname est <brand> <product_type> vous pouvez simplement supprimer tous les produits avec le nom de produit not like '<brand>%' .

Dans le cas ci-dessus ne va pas aider – y at-il des règles de nommage des produits?

Comme l'idée ci-dessus ne peut pas être appliquée, créez une fonction Split :

 CREATE FUNCTION [dbo].[Split] ( @Ssortingng NVARCHAR(4000), @Delimiter NCHAR(1) ) RETURNS TABLE AS RETURN ( WITH Split(stpos,endpos) AS( SELECT 0 AS stpos, CHARINDEX(@Delimiter,@Ssortingng) AS endpos UNION ALL SELECT endpos+1, CHARINDEX(@Delimiter,@Ssortingng,endpos+1) FROM Split WHERE endpos > 0 ) SELECT 'Id' = ROW_NUMBER() OVER (ORDER BY (SELECT 1)), 'Data' = SUBSTRING(@Ssortingng,stpos,COALESCE(NULLIF(endpos,0),LEN(@Ssortingng)+1)- stpos) FROM Split ) 

Et utilisez-le dans la requête:

 select (SELECT (', ' + Data) FROM Split(t.textVal, ' ') order by [Data] FOR XML PATH( '' ) ) from test t 

Cela vous fournira le nom du produit avec des mots sortingés. Avec cela, vous pouvez facilement find des duplicates. La deuxième requête est rude sur les bords comme je dois aller afk, mais vous devriez arriver à lisser 🙂 Bonne chance

voici une solution pour deux ou plusieurs mots séparés par l'espace. Fondamentalement, l'idée est d'utiliser un CTE récursif pour split par l'espace, puis pour xml de mettre les noms set sortingés. Ensuite, vous pouvez regrouper par la nouvelle colonne de nom pour get votre list dédupliquée:

 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