如何使用 LEFT OUTER JOIN 来识别供应商缺少的产品?

How to use LEFT OUTER JOIN to identify missing products by vendor?

这个查询很有趣,但我已经到了需要帮助的地方。

我有好几张表,最终问题是:

和/或

缺失: 尚未被供应商使用(参见查询 1)。

请注意,零件不归因于产品或供应商,因为这两者都可能根据季节而变化,而且零件通常会激发产品的实际含义。

基本上,每个供应商要注意的部分是我们试图在高层次上回答的问题,以确定哪些供应商在哪些类别中缺失的部分最多?

现在,我确实有了我需要的第一个查询,效果很好。它的作用是在我指定特定供应商时按类别告诉我缺少的部分。

这是数据库创建脚本和工作查询的SQLfiddle:

查询 1:

http://sqlfiddle.com/#!9/088e7/1

以及查询:

SELECT 
    c.name AS category,
    COUNT(pt.id) AS parts,
    COUNT(CASE WHEN in_stock IS NULL THEN pt.id END) AS missing_parts
FROM 
    season AS s
LEFT OUTER JOIN 
    ( 

        SELECT 
            s.id AS season_id,
            s.type season_type,
            max(i.in_stock) AS in_stock
        FROM
            inventory AS i
            JOIN season      AS s  ON i.season_id = s.id
            JOIN product     AS p  ON i.product_id = p.id
            JOIN vendor      AS v  ON p.vendor_id = v.id
            JOIN part        AS pt ON s.part_id = pt.id

        WHERE
            v.id = 2
            AND
            s.type = 'Type A'
        GROUP BY
            1,2) AS seas   ON seas.season_id = s.id AND seas.season_type = s.type
            JOIN part      AS pt ON pt.id = s.part_id
            JOIN part_data AS pd ON pt.id = pd.part_id
            JOIN category  AS c  ON pt.category_id = c.id
    WHERE
        s.type = 'Type A'
GROUP BY
    1;

上面的工作很有魅力,结果如下:

| name      | parts | missing_parts |
|-----------|-------|---------------|
| category3 | 3     | 2             |
| category4 | 2     | 0             |
| category5 | 2     | 2             |
| category6 | 3     | 3             |

我的问题是,当我尝试使用供应商而不是类别进行类似查询时,同时删除了供应商过滤器。在下面SQLfiddle中,大家可以看到,因为零件确实少了,当然不能像我这样查询时归因于供应商。

http://sqlfiddle.com/#!9/088e7/2

他们查询 2:

SELECT 
    seas.vendor AS vendor,
    COUNT(pt.id) AS parts,
    COUNT(CASE WHEN in_stock IS NULL THEN pt.id END) AS missing_parts 
FROM 
    season AS s
LEFT OUTER JOIN     
 (SELECT 
            s.id AS season_id,
            v.name AS vendor, 
            s.type season_type,
            max(i.in_stock) AS in_stock
        FROM
            inventory AS i
            JOIN season      AS s  ON i.season_id = s.id
            JOIN product     AS p  ON i.product_id = p.id
            JOIN vendor      AS v  ON p.vendor_id = v.id
            JOIN part        AS pt ON s.part_id = pt.id

        WHERE
            s.type = 'Type A'
        GROUP BY
            1,2          ) AS seas   ON seas.season_id = s.id AND seas.season_type = s.type
            JOIN part      AS pt     ON pt.id = s.part_id
            JOIN part_data AS pd     ON pt.id = pd.part_id
            JOIN category  AS c      ON pt.category_id = c.id

    AND
        s.type = 'Type A'
GROUP BY
    1;

查询 2 的结果:

| vendor   | parts | missing_parts |
|----------|-------|---------------|
| (null)   | 4     | 4             |
| Vendor 1 | 2     | 0             |
| Vendor 2 | 3     | 0             |
| Vendor 3 | 2     | 0             |
| Vendor 4 | 2     | 0             |
| Vendor 5 | 2     | 0             |

请注意空值,这是有意义的,因为这些是我正在寻找的 "missing" 部分,不能归因于供应商。

我想知道的是,是否有将缺失部分计数添加到附加列中的方法?

所需输出中的缺失部分列很难准确,因为这也是此查询的重点,我不知道......即使有这么少量的数据。再次注意,缺少的部分没有供应商,但这是我最好的照片。

| vendor   | parts | missing_parts |
|----------|-------|---------------|
| Vendor 1 | 2     | 1             |
| Vendor 2 | 3     | 1             |
| Vendor 3 | 2     | 3             |
| Vendor 4 | 2     | 0             |
| Vendor 5 | 2     | 2             | 

在理想情况下,我还可以添加类别:

| category   | vendor   | parts | missing_parts |
|------------|----------|-------|---------------|
| category 1 | Vendor 1 | 2     | 1             |
| category 1 | Vendor 2 | 3     | 1             |
| category 1 | Vendor 3 | 2     | 3             |
| category 1 | Vendor 4 | 2     | 0             |
| category 1 | Vendor 5 | 2     | 2             |
| category 2 | Vendor 1 | 1     | 1             |
| category 2 | Vendor 2 | 1     | 1             |
| category 2 | Vendor 3 | 0     | 3             |
| category 2 | Vendor 4 | 2     | 0             |
| category 2 | Vendor 5 | 0     | 2             |

问题是第二个查询在 LEFT JOIN 中加入的子查询 (vendor) 的字段上有一个 GROUP BY,因此它将创建一个每个供应商的输出行(包括 NULL 季节中与子查询不匹配的行)。

更具体地说 - 您的 count

COUNT(CASE WHEN in_stock IS NULL THEN pt.id END) AS missing_parts

(我更喜欢写SUM(in_stock IS NULL)

但由于 in_stock 是每个 vendor 的聚合结果 - 你永远不会有 NULL 值。 (check the sub-query results)

我认为您应该明确查询的目标。例如 - 第一个返回 -

每个类别在给定季节中的零件数量,以及 该类别不可用的季节数量(而不是缺失零件的数量,因为子查询没有连接类别)。

如果我明白你在寻找什么,我会首先从你最终要寻找的东西开始..

不同部分和类别的列表。那么你正在寻找谁错过了什么。要做到这一点,这基本上是每个供应商反对这个 "master list of parts/categories" 和谁 does/not 拥有它的笛卡尔。

SELECT DISTINCT 
      pt.id, 
      pt.category_id
   from 
      part pt

现在,考虑第二部分。特定 VENDOR 有哪些可能的零件和类别。

SELECT DISTINCT 
      pt.id, 
      pt.category_id, 
      p.vendor_id
   FROM
      season s 
         JOIN inventory i
            ON s.id = i.season_id
            JOIN product p  
               ON i.product_id = p.id
         JOIN part pt 
            ON s.part_id = pt.id

在上面的 tables 中,我不需要类别或实际加入的供应商 tables,因为我只关心谁拥有什么的合格 ID。首先,所有可能的零件 ID 和类别 ID,但在第二个中,我们还获取拥有它的 VENDOR ID。

现在,在没有任何 "ON" 条件的情况下,将以供应商 JOINED 开始的部分连接到类别中。需要连接以允许 "v.id" 作为语法中的较低连接,这将为我提供应用/测试到每个类别的每个供应商的笛卡尔坐标。然后,类别 table 连接到所有不同的部分,最后左连接到不同的部分查询 PER VENDOR

最后,添加您的聚合和分组依据。由于左连接,如果存在 VndParts.ID,则记录确实存在,因此 Vendor Parts FOUND 计数增加。如果供应商零件 ID 为 NULL,则缺少零件计数(因此我的总和 case/when)。

SELECT
      v.name Vendor,
      c.name  category,
      count( PQParts.ID ) TotalAvailableParts,
      count( VndParts.ID ) VendorParts,
      sum( case when VndParts.ID IS NULL then 1 else 0 end ) MissingParts
   from
      vendor v JOIN
      category c
         JOIN 
         ( SELECT DISTINCT 
                 pt.id, 
                 pt.category_id
              from 
                 part pt ) PQParts
            ON c.id = PQParts.category_id 
            LEFT JOIN
            ( SELECT DISTINCT 
                    pt.id, 
                    pt.category_id, 
                    p.vendor_id
                 FROM
                    season s 
                       JOIN inventory i
                          ON s.id = i.season_id
                          JOIN product p  
                             ON i.product_id = p.id
                       JOIN part pt 
                          ON s.part_id = pt.id ) VndParts
               ON v.id = VndParts.vendor_id
               AND PQParts.ID = VndParts.ID
               AND PQParts.Category_ID = VndParts.Category_ID
   group by
      v.name,
      c.name

Applied against your SQL-Fiddle sample database construct

现在,即使您创建了类别 1-6 的示例数据,您的所有 PARTS 都只定义了类别 3-6,如我的示例数据结果。我无法根据

的示例查询强制使用不存在的数据
SELECT
      *
   from
      category c
         JOIN 
         ( SELECT DISTINCT 
                 pt.id, 
                 pt.category_id
              from 
                 part pt ) PQParts
            ON c.id = PQParts.category_id 

如果确实存在这样的实际数据,那么也会显示其他类别的缺失部分。

现在是最后的说明。您还在寻找特定的 SEASON。我只想添加一个 WHERE 子句以适应 VndParts 查询中的内容。然后更改 PQParts 查询以包括季节连接,例如

SELECT DISTINCT 
      pt.id, 
      pt.category_id
   from 
      part pt

现在,考虑第二部分。特定 VENDOR 有哪些可能的零件和类别。

SELECT DISTINCT 
      pt.id, 
      pt.category_id
   FROM
      season s 
         JOIN part pt 
            ON s.part_id = pt.id
   WHERE
      s.type = 'Type A'

要进一步限制特定供应商,添加供应商子句非常简单,因为它是供应商 "v" 在外部标准的基础,供应商参考第二个 LEFT- JOIN 也有可供过滤掉的供应商别名。

根据您的描述,您似乎想要计算每个供应商在每个类别中有多少零件可以列为产品但没有列为产品。 这基本上是每个类别可以列出多少零件与实际列出多少零件之间的区别。 因此,您可以将可能的连接数和左连接数计算为实际连接数。

基于 sqlfiddle,下面的代码还假定您希望能够专注于一种季节类型,并且只有 partdata 中列出的部分(有销售?)是相关的。

   select c.name as category
      , v.name as vendor
      , cpartcount.parts
      , cpartcount.parts-coalesce(cvpartcount.parts,0) as missingparts
   from vendor v
   cross join
   ( 
     select pt.category_id, count(pt.id) as parts
     from part pt 
     where pt.id in 
     (
       select s.part_id
       from season s 
       where s.type='Type A'
     )
     and pt.id in 
     (
       select pd.part_id
       from part_data pd
     )
     group by pt.category_id
   ) cpartcount
   join category c
   on cpartcount.category_id=c.id
   left join
   ( 
      select pt.category_id, v.id as vendor_id, count(pt.id) as parts
      from part pt,vendor v
      where (v.id,pt.id) IN
      (
        select p.vendor_id, s.part_id
        from product p
        join inventory i
        on p.id=i.product_id
        join season s 
        on i.season_id = s.id
        join part_data pd
        on s.part_id=pd.part_id
        where s.type='Type A'   
      ) 
      group by pt.category_id,v.id
   ) as cvpartcount
   on cpartcount.category_id=cvpartcount.category_id
   and v.id=cvpartcount.vendor_id