Объединение таблиц и замена нулевых значений указанными значениями - PullRequest
1 голос
/ 16 января 2012

Я пытаюсь сделать страницу для печати, где есть все продажи указанного производителя, перечисляя все продукты, между указанными датами. Если продаж не было, должно отображаться 0.

Таблицы

// Manufacturer table
// mid, manufacturer
// Products table
// pid, product, ref_manufacturer_id
// Orders table
// oid, orderPrice, orderDateTime, ref_product_id

И запрос, который работает (без ограничения даты)

SELECT prod.product, COALESCE(COUNT(pord.oid),0) AS orderCount,
COALESCE(SUM(pord.orderPrice),0) AS orderSum
FROM product_manufacturer AS manu
JOIN product_list AS prod ON prod.ref_manufacturer_id = manu.mid
LEFT JOIN product_orders AS pord ON pord.ref_product_id = prod.pid
WHERE manu.mid = :manu_id
GROUP BY prod.product;

Но как только я добавлю в WHERE-синтаксис это

WHERE manu.mid = :manu_id AND DATE(pord.orderDateTime) BETWEEN :orders_start AND :orders_end

Я использую PHP PDO при подключении и проверке того, что manu_id имеет тип int, а orders_start / end преобразуется в формат даты MySQL.

Но вопрос, который я пытаюсь выяснить, заключается в том, что вызывает проблему: когда я добавляю ограничение даты, каждый продукт, который не был заказан, не отображается на выходе?

SQL при создании таблиц

CREATE TABLE product_list (
  pid bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  product varchar(255) NOT NULL,
  ref_manufacturer_id bigint(20) unsigned NOT NULL,
  PRIMARY KEY (pid),
  KEY ref_manufacturer_id (ref_manufacturer_id)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;

CREATE TABLE product_manufacturer (
  mid bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  manufacturer varchar(255) NOT NULL,
  PRIMARY KEY (mid),
  UNIQUE KEY manufacturer (manufacturer)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;

CREATE TABLE product_orders (
  oid bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  orderPrice float(10,2) NOT NULL,
  orderDatetime timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  red_product_id bigint(20) unsigned NOT NULL,
  PRIMARY KEY (oid),
  KEY red_product_id (red_product_id)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;

Ответы [ 4 ]

2 голосов
/ 16 января 2012

Вам нужно переместить критерии orderDateTime в предложение join вместо предложения where, например:

SELECT prod.product, COALESCE(COUNT(pord.oid),0) AS orderCount,
       COALESCE(SUM(pord.orderPrice),0) AS orderSum
FROM product_manufacturer AS manu
JOIN product_list AS prod ON prod.ref_manufacturer_id = manu.mid
LEFT JOIN product_orders AS pord 
          ON pord.ref_product_id = prod.pid
          AND DATE(pord.orderDateTime) BETWEEN :orders_start AND :orders_end
WHERE manu.mid = :manu_id
GROUP BY prod.product;

Причина, по которой он не работает в предложении WHERE, заключается в том, что значения NULL возвращаются из внешнего соединения. Если у вас нет строки в product_orders для продукта, внешнее объединение возвращает NULL для поля даты orderDateTime, и эта строка будет отфильтрована, поскольку NULL не равно ничему.

2 голосов
/ 16 января 2012

Попробуйте:

SELECT p.product, 
    COALESCE(o.orderCount, 0) as orderCount, 
    COALESCE(o.orderSum,0) AS orderSum
FROM product_manufacturer AS m
JOIN product_list AS p ON p.ref_manufacturer_id = m.mid
LEFT JOIN (
    SELECT ref_product_id as pid, COUNT(oid) AS orderCount, SUM(orderPrice) AS orderSum
    FROM product_orders
    WHERE DATE(orderDateTime) BETWEEN :orders_start AND :orders_end
    GROUP BY ref_product_id
) AS o ON p.pid = o.pid
WHERE m.mid = :manu_id

Редактировать : исправлено после комментария ypercube.

0 голосов
/ 16 января 2012

Я не знаю, как работает ваша конкретная система, но она может быть orderDateTime не установлена ​​(т.е. NULL или что-то еще), пока этот продукт не будет заказан. Вы можете попробовать:

WHERE manu.mid = :manu_id AND ((DATE(pord.orderDateTime) BETWEEN :orders_start AND :orders_end) OR pord.orderDateTime=NULL)

Если это не так, не могли бы вы привести пример значения orderDateTime для чего-то, что не появляется, когда вы этого хотите?

0 голосов
/ 16 января 2012

попробуйте это в предложении where.

WHERE manu.mid = :manu_id AND (DATE(pord.orderDateTime) BETWEEN :orders_start AND :orders_end)

Возможно, вторая функция AND будет считана как другая инструкция where, чтобы оператор возвращал true.Просто догадка об этом.Дайте мне знать, если это поможет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...