简体   繁体   中英

MySQL Join and excluding rows with count=0

I have the following straightforward query which is part of a garbage collection script. The script is supposed to delete unused shopping carts. A cart is unused when it was updated more than 24 hours ago and is not attached to an order or a user.

        $query = "SELECT comm_cart.id AS `cart_id`, (" . time() . " - comm_cart.update_date) AS `diff`, COUNT(comm_orders.cart_id) AS `c1`, COUNT(comm_users.cart_id) AS `c2` " .
             "FROM `comm_cart` " . 
             "LEFT JOIN `comm_orders` ON comm_cart.id=comm_orders.cart_id " . 
             "LEFT JOIN `comm_users` ON comm_cart.id=comm_users.cart_id " . 
             "GROUP BY comm_cart.id "; 
             "HAVING `diff`>86400 AND `c1`=0 AND `c2`=0"; 

The query finds too many carts: it also tags the ones that have a c1>0 or c2>0 and I can't figure out why. Any clues?

I suspect that you are joining along two different dimensions. The easy fix is to use distinct :

SELECT comm_cart.id AS `cart_id`, (" . time() . " - comm_cart.update_date) AS `diff`, 
       COUNT(DISTINCT comm_orders.cart_id) AS `c1`, COUNT(DISTINCT comm_users.cart_id) AS `c2` " .

The better solution would be to use not exists for these two conditions:

FROM comm_carts cc
WHERE not exists (select 1 from comm_orders co where cc.id = co.cart_id )
      not exists (select 1 from comm_users cu where cc.id = cu.cart_id )

You don't even need the group by, an isnull condition in the where would work wonders, of course I suggest using Gordon's suggestion of the not exists, but if you want the minimum change this would be is.

SELECT
    comm_cart.id AS `cart_id`,
    (UNIX_TIMESTAMP() - comm_cart.update_date) AS `diff`
FROM `comm_cart`
LEFT JOIN `comm_orders`
  ON comm_cart.id=comm_orders.cart_id
LEFT JOIN `comm_users`
  ON comm_cart.id=comm_users.cart_id
WHERE
  comm_orders.cart_id IS NULL
  AND
  comm_users.cart_id IS NULL

Oh, and I've used UNIX_TIMESTAMP() instead of the PHP time function, same effect, but this avoids mixing PHP and SQL.

if you only want to get data with c1=0 and c2=0 then you need to write a where condition instead of having before group by,

$query = "SELECT comm_cart.id AS `cart_id`, (" . time() . " - comm_cart.update_date) AS   `diff`, COUNT(comm_orders.cart_id) AS `c1`, COUNT(comm_users.cart_id) AS `c2` " .
         "FROM `comm_cart` " . 
         "LEFT JOIN `comm_orders` ON comm_cart.id=comm_orders.cart_id " . 
         "LEFT JOIN `comm_users` ON comm_cart.id=comm_users.cart_id " . 
         " where c1=0 and c2 =0 and diff >86400 GROUP BY comm_cart.id;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM