2009-11-20 5 views
2

Voici un sous-ensemble de mes tableaux:Optimize requête SQL pour les commandes annulées

orders: 
- order_id 
- customer_id 

order_products: 
- order_id 
- order_product_id (unique key) 
- canceled 

Je veux sélectionner toutes les commandes (ORDER_ID) pour un client donné (customer_id), où tous les produits de l'ordre sont annulé, pas seulement certains des produits. Y at-il une façon plus élégante ou efficace de le faire que cela:

select order_id from orders 
where order_id in (
    select order_id from orders 
    inner join order_products on orders.order_id = order_products.order_id 
    where order_products.customer_id = 1234 and order_products.canceled = 1 
) 
and order_id not in (
    select order_id from orders 
    inner join order_products on orders.order_id = order_products.order_id 
    where order_products.customer_id = 1234 and order_products.canceled = 0 
) 
+2

Quel est le moteur de base de données utilisez-vous, par exemple MySql, sql-server-2000, sql-server-2005, Oracle, etc. – chadhoc

Répondre

2

Si toutes les commandes ont au moins une ligne dans order_products, Essayez cette

Select order_id from orders o 
Where Not Exists 
     (Select * From order_products 
     Where order_id = o.order_id 
      And cancelled = 1) 

Si l'hypothèse ci-dessus n'est pas vrai, vous avez également besoin:

Select order_id from orders o 
Where Exists 
     (Select * From order_products 
     Where order_id = o.order_id) 
    And Not Exists 
     (Select * From order_products 
     Where order_id = o.order_id 
      And cancelled = 1) 
0
SELECT customer_id, order_id, count(*) AS product_count, sum(canceled) AS canceled_count 
FROM orders JOIN order_products 
ON orders.order_id = order_products.order_id 
WHERE customer_id = <<VALUE>> 
GROUP BY customer_id, order_id 
HAVING product_count = canceled_count 
1

Le moyen le plus rapide sera la suivante:

SELECT order_id 
FROM orders o 
WHERE customer_id = 1234 
     AND 
     (
     SELECT canceled 
     FROM order_products op 
     WHERE op.order_id = o.order_id 
     ORDER BY 
       canceled DESC 
     LIMIT 1 
     ) = 0 

La sous-requête renverra 0 si et seulement si certains produits ont été annulés.

S'il n'y avait aucun produit, la sous-requête renvoie NULL; S'il y a au moins un produit non-annulé, la sous-requête renvoie 1.

Assurez-vous que vous avez un index sur order_products (order_id, canceled)

1

Quelque chose comme ça? Cela suppose que chaque commande a au moins un produit, sinon cette requête retournera aussi les commandes sans aucun produit.

select order_id 
from orders o 
where not exists (select 1 from order_products op 
        where canceled = 0 
        and op.order_id = o.order_id 
) 
and o.customer_id = 1234 
0

Vous pouvez essayer quelque chose comme ça

select orders.order_id 
from @orders orders inner join 
     @order_products order_products on orders.order_id = order_products.order_id 
where order_products.customer_id = 1234 
GROUP BY orders.order_id 
HAVING SUM(order_products.canceled) = COUNT(order_products.canceled) 
0

Puisque nous ne savons pas la plate-forme de base de données, voici une approche standard ANSI. Notez que cela ne suppose rien au sujet du schéma (type de données du champ annulé, comment le drapeau annulé est défini (ie 'OUI', 1, etc.)) et n'utilise rien de spécifique à une plate-forme de base de données donnée (qui serait probablement approche plus efficace si vous pouviez nous donner la plate-forme et la version que vous utilisez):

select op1.order_id 
from (
      select op.order_id, cast(case when op.cancelled is not null then 1 else 0 end as tinyint) as is_cancelled 
      from #order_products op 
     ) op1 
group by op1.order_id 
having count(*) = sum(op1.is_cancelled);