2010-10-23 30 views
5

que tienen una visión de dos columnastodas las posibles combinaciones de dos datos de la columna

Product Id Tag 
---------------------- 
1   Leather 
1   Watch 
2   Red 
2   Necklace 
2   Pearl 

Estoy tratando de obtener todas las combinaciones posibles de etiquetas para un producto como tal:

1   Leather 
1   Leather,Watch 
2   Pearl 
2   Pearl,Necklace 
2   Pearl Necklace,Red 
2   Necklace 
2   Necklace, Red 
2   Red 

tengo encontrado y me robaron algunos SQL que me dan la lista completa para todas las versiones, pero no para las pequeñas, a continuación.

Cualquier idea, ha empezado a dolerme la cabeza. Una pinta virtual para la mejor respuesta.

SELECT ProductId, 
     (SELECT CAST(Tag + ', ' AS VARCHAR(MAX)) 
      FROM ProductByTagView 
     WHERE Product.ProductId = ProductByTagView.ProductId 
     order by tag 
     FOR XML PATH ('')) AS Tags 
FROM Product 
+0

Generar todas las combinaciones posibles es HARD en SQL; es mejor que lo hagas en un lenguaje de cliente, como PHP, Perl o C# – Andomar

+1

El principal problema es que el número de combinaciones crece exponencialmente. Hay (2^n -1) combinaciones de al menos una etiqueta. (Te falta 'Red, Pearl' de tu lista donde n = 3). ¿Qué tan grande puede llegar? –

+0

@Andomar Creo que podría estar en la respuesta. Como mencioné a continuación, tengo 40 productos extraños y 50 etiquetas ish, cada producto podría tener en teoría las 50 etiquetas, pero en la práctica no obtendría mucho por encima de 10. –

Respuesta

0

Algo como esto ?:

 
select a.ProductID, a.tag+','+b.tag from aView a 
cross join aView b 
where a.tag != b.tag 
union 
select ProductID, tag from aView 
+0

esto no devuelve los resultados correctos –

3

Ésta es una forma.

En teoría, puede hacer frente a hasta 20 etiquetas por Producto (limitado por el tamaño de la tabla de números). Sin embargo, no me molesté en intentarlo. En mi computadora de escritorio, tardó unos 30 segundos en generar los 65.535 resultados de un solo producto con 16 etiquetas. ¡Con suerte, su número real de etiquetas por producto será mucho menor que eso!

IF OBJECT_ID('tempdb..#Nums') IS NULL 
BEGIN 
CREATE TABLE #Nums 
(
i int primary key 
) 

;WITH 
L0 AS (SELECT 1 AS c UNION ALL SELECT 1),  
L1 AS (SELECT 1 AS c FROM L0 A CROSS JOIN L0 B), 
L2 AS (SELECT 1 AS c FROM L1 A CROSS JOIN L1 B), 
L3 AS (SELECT 1 AS c FROM L2 A CROSS JOIN L2 B), 
L4 AS (SELECT 1 AS c FROM L3 A CROSS JOIN L3 B), 
L5 AS (SELECT 1 AS c FROM L4 A CROSS JOIN L4 B), 
Nums AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 0)) AS i FROM L5) 

INSERT INTO #Nums 
SELECT TOP 1048576 i FROM Nums; 
END 


;with ProductTags As 
(
SELECT 1 ProductId,'Leather' AS Tag UNION ALL 
SELECT 1, 'Watch' UNION ALL 
SELECT 2, 'Red' UNION ALL 
SELECT 2, 'Necklace' UNION ALL 
SELECT 2, 'Pearl' 
), NumberedTags AS 
(
SELECT 
     ProductId,Tag, 
     ROW_NUMBER() OVER (PARTITION BY ProductId ORDER BY Tag) rn, 
     COUNT(*) OVER (PARTITION BY ProductId) cn 
FROM ProductTags 
), 
GroupedTags As 
(
SELECT ProductId,Tag,i 
FROM NumberedTags 
JOIN #Nums on 
       #Nums.i < POWER (2 ,cn) 
      and #Nums.i & POWER (2 ,rn-1) > 0 
) 
SELECT ProductId, 
     STUFF((SELECT CAST(', ' + Tag AS VARCHAR(MAX)) 
      FROM GroupedTags g2 
     WHERE g1.ProductId = g2.ProductId and g1.i = g2.i 
     ORDER BY Tag 
     FOR XML PATH ('')),1,1,'') AS Tags 
FROM GroupedTags g1 
GROUP BY ProductId, i 
ORDER BY ProductId, i 

devoluciones

ProductId Tags 
----------- ------------------------------ 
1   Leather 
1   Watch 
1   Leather, Watch 
2   Necklace 
2   Pearl 
2   Necklace, Pearl 
2   Red 
2   Necklace, Red 
2   Pearl, Red 
2   Necklace, Pearl, Red 
+0

Martin, gracias por esto pero en mi DB de producción (40,000 productos ish hasta 15 etiquetas de 50 por producto) esto tomó una hora para obtener 415 productos. –

+0

Como @Andomar comentó anteriormente, creo que una mezcla de C# (mi arma elegida) y SQL podría ser la respuesta. Sin embargo, muchas gracias por esto, amo esta comunidad. –

0

Mi respuesta enlatada:

suponga que tiene una tabla de números auxiliar con números enteros.

DECLARE @s VARCHAR(5); 
SET @s = 'ABCDE'; 

WITH Subsets AS (
SELECT CAST(SUBSTRING(@s, Number, 1) AS VARCHAR(5)) AS Token, 
CAST('.'+CAST(Number AS CHAR(1))+'.' AS VARCHAR(11)) AS Permutation, 
CAST(1 AS INT) AS Iteration 
FROM dbo.Numbers WHERE Number BETWEEN 1 AND 5 
UNION ALL 
SELECT CAST(Token+SUBSTRING(@s, Number, 1) AS VARCHAR(5)) AS Token, 
CAST(Permutation+CAST(Number AS CHAR(1))+'.' AS VARCHAR(11)) AS 
Permutation, 
s.Iteration + 1 AS Iteration 
FROM Subsets s JOIN dbo.Numbers n ON s.Permutation NOT LIKE 
'%.'+CAST(Number AS CHAR(1))+'.%' AND s.Iteration < 5 AND Number 
BETWEEN 1 AND 5 
--AND s.Iteration = (SELECT MAX(Iteration) FROM Subsets) 
) 
SELECT * FROM Subsets 
WHERE Iteration = 5 
ORDER BY Permutation 

Token Permutation Iteration 
----- ----------- ----------- 
ABCDE .1.2.3.4.5. 5 
ABCED .1.2.3.5.4. 5 
ABDCE .1.2.4.3.5. 5 
(snip) 
EDBCA .5.4.2.3.1. 5 
EDCAB .5.4.3.1.2. 5 
EDCBA .5.4.3.2.1. 5 
(120 row(s) affected) 
0

Como sugirió @Andomar este resultó ser un problema más resuelto en un idioma del cliente, probé las diversas soluciones (gracias chicos) y aunque parecía que había algo allí, sobre todo en la respuesta de Martin era el problema el tiempo necesario para ejecutar la consulta

Gracias de nuevo chicos.

Alec

Cuestiones relacionadas