2010-06-30 7 views
6

En SQL Server 2005, tengo una tabla de detalles de la orden con una ID de pedido y una ID de producto. Quiero escribir una declaración SQL que encuentre todas las órdenes que tienen todos los artículos dentro de un orden en particular. Entonces, si la orden 5 tiene los ítems 1, 2 y 3, me gustaría todas las demás órdenes que también tengan 1, 2 y 3. Además, si la orden 5 tuvo 2 veces y 3 una vez, desearía todas las otras órdenes con dos 2s y un 3.Instrucción SQL para seleccionar un grupo que contiene todo un conjunto de valores

Mi preferencia es que devuelva las órdenes que coincidan exactamente, pero las órdenes que son un superconjunto son aceptables si eso es mucho más fácil/funciona mucho mejor.

que intentaron una autocombinación como la siguiente, pero que las órdenes que se encuentran con cualquier de los artículos en lugar de toda de los artículos.

SELECT * FROM Order O1 
JOIN Order O2 ON (O1.ProductId = O2.ProductId) 
WHERE O2.OrderId = 5 

Esto también me dio duplicados si la orden 5 contenía el mismo artículo dos veces.

+0

Si la persona ordena los múltiplos del artículo n. ° 1, entonces estaría en los Detalles del pedido como varias filas, correctas. No hay una columna de "cantidad"? –

+0

Correcto - sin columna de cantidad –

Respuesta

3

Si la tabla OrderDetails contiene una restricción única en OrderId y ProductId, entonces usted puede hacer algo como esto:

Select ... 
From Orders As O 
Where Exists (
       Select 1 
       From OrderDetails As OD1 
       Where OD1.ProductId In(1,2,3) 
        And OD1.OrderId = O.Id 
       Group By OD1.OrderId 
       Having Count(*) = 3 
       ) 

Si es posible tener el mismo ProductId en el mismo orden varias veces, entonces podría cambiar la cláusula Having a Count(Distinct ProductId) = 3

Ahora, dado lo anterior, si desea que la situación en la que cada pedido tiene la misma firma con entradas de producto duplicadas, eso es más complicado. Para hacer eso necesitaría la firma del pedido en cuestión sobre los productos en cuestión y luego consultar para esa firma:

With OrderSignatures As 
    (
    Select O1.Id 
     , (
      Select '|' + Cast(OD1.ProductId As varchar(10)) 
      From OrderDetails As OD1 
      Where OD1.OrderId = O1.Id 
      Order By OD1.ProductId 
      For Xml Path('') 
      ) As Signature 
    From Orders As O1 
    ) 
Select ... 
From OrderSignatures As O 
    Join OrderSignatures As O2 
     On O2.Signature = O.Signature 
      And O2.Id <> O.Id 
Where O.Id = 5 
+0

Creo que está intentando emparejarlo en función de los elementos contenidos en otra ID de pedido, no en función de una lista estática de elementos (eso sería lo suficientemente fácil de hacer con una sola combinación por artículo). –

+0

@ Adam Robinson - Veo eso. Expandí mi respuesta para incluir ese tipo de solicitud. – Thomas

+0

+1. No había pensado en usar un CTE y XML para crear la firma. Mucho más elegante, aunque tal vez un poco más difícil de entender. –

1

Este tipo de cosas es muy difícil de hacer en SQL, ya que SQL está diseñado para generar su conjunto de resultados, en el nivel más básico, comparando un conjunto de valores de columna en una sola fila con otro valor. Lo que intenta hacer es comparar un solo valor de columna (o un conjunto de valores de columna) en varias filas con otro conjunto de filas múltiples.

Para hacer esto, tendrá que crear algún tipo de firma de orden. Estrictamente hablando, esto no es posible haciendo solo la sintaxis de la consulta; tendrás que usar algo de T-SQL.

declare @Orders table 
(
    idx int identity(1, 1), 
    OrderID int, 
    Signature varchar(MAX) 
) 
declare @Items table 
(
    idx int identity(1, 1), 
    ItemID int, 
    Quantity int 
) 

insert into @Orders (OrderID) select OrderID from [Order] 

declare @i int 
declare @cnt int 

declare @j int 
declare @cnt2 int 

select @i = 0, @cnt = max(idx) from @Orders 

while @i < @cnt 
begin 
    select @i = @i + 1 

    declare @temp varchar(MAX) 

    delete @Items 

    insert into @Items (ItemID, Quantity) 
    select 
     ItemID, 
     Count(ItemID) 

    from OrderItem oi  

    join @Orders o on o.idx = @i and o.OrderID = oi.OrderID 

    group by oi.ItemID 

    order by oi.ItemID 

    select @j = min(idx) - 1, @cnt2 = max(idx) from @Items 

    while @j < @cnt2 
    begin 
     select @j = @j + 1 

     select @temp = isnull(@temp + ', ','') + 
      '(' + 
      convert(varchar,i.ItemID) + 
      ',' + 
      convert(varchar, i.Quantity) + 
      ')' 
     from @Items i where idx = @j 
    end 

    update @Orders set Signature = @temp where idx = @i 

    select @temp = null 
end 

select 
    o_other.OrderID 

from @Orders o 

join @Orders o_other on 
     o_other.Signature = o.Signature 
    and o_other.OrderID <> o.OrderID 

where o.OrderID = @OrderID 

Esto supone (basado en el texto de su pregunta) que ordena múltiplo del mismo artículo en una orden dará lugar a varias filas, en lugar de utilizar una columna Quantity. Si este último es el caso, simplemente elimine el group by de la consulta de población @Items y reemplace Count(ItemID) con Quantity.

+0

Me hizo sentir mejor al comenzar con "este tipo de cosas es muy difícil de hacer en SQL". :) –

+0

LOL. Lo suficientemente justo. :) –

1

Creo que esto debería funcionar. Estoy usando 108 como un OrderID de ejemplo, por lo que tendrá que reemplazarlos dos veces más abajo o usar una variable.

WITH TempProducts(ProductID) AS 
(
    SELECT DISTINCT ProductID FROM CompMarket 
    WHERE OrderID = 108 
) 
SELECT OrderID FROM CompMarket 
WHERE ProductID IN (SELECT ProductID FROM TempProducts) 
AND OrderID != 108 
GROUP BY OrderID 
HAVING COUNT(DISTINCT ProductID) >= (SELECT COUNT(ProductID) FROM TempProducts) 

Este utiliza un CTE para obtener una lista de los productos de un pedido, a continuación, selecciona todos los ID de compra que tienen productos que son todos en esta lista. Para asegurarse de que los Pedidos devueltos tengan todos los productos, esto compara el Recuento del CTE con los Recuentos de los Productos del Pedido devuelto.

+0

Tenga en cuenta que esto no funcionará para pedidos con múltiples del mismo artículo. Por ejemplo, si la orden '1' tiene dos elementos' 10' y uno '20', mientras que la orden' 2' tiene un elemento '10' y dos' 20', este código dará como resultado una coincidencia. –

+0

¿No encontraría eso alguna orden que tenga solo algunos de los mismos elementos, siempre que coincidan los recuentos? Entonces, si la orden 108 tenía 3 elementos, ¿podría encontrar 3 (o más) pedidos de artículos con al menos un artículo de 108? –

+0

@ Adam. Agregué DISTINCT a COUNT (DISTINCT ...), lo cual resolverá ese problema. @ Eddie, no: solo está comparando el COUNT de los artículos que están EN, no todos los productos de ese pedido. – Nick

Cuestiones relacionadas