Tricky SQL Join,甚至是可能的吗?

时间:2017-01-12 10:49:10

标签: mysql

我尝试解决一段困扰我一段时间的MySQL查询问题。我只能使用脚本来解决这个问题,而不是单个查询,这有点烦人。

我有以下表格:

凭证:     VoucherID,VoucherDate,姓名,地址......

Voucher_Item:     VoucherItemID,VoucherID,ArticleNr,Amount,Price ......

由于表格可能会告诉您这是一个在线商店,我们这里有订单和订单文章,每个订单通过VoucherID链接为FK。所以我可以使用

SELECT ...
FROM Voucher v
LEFT JOIN Voucher_Item vi USING(VoucherID)
WHERE ...

到目前为止一切顺利。现在我的构造不好(我没有做到这一点,但我必须使用它),订单的邮资被视为一篇文章。

每个订单都有一个特定的,唯一的 - ArticleNr' ponat1'。

因此,每个订单至少包含一行或多行文章,其中可以包含" ArticleNr =' ponat1',此情况下的价格将包含邮资

这意味着邮资的订单如下:

Table Voucher:
VoucherID | VoucherDate | Name       | Address ...
1         |  2017-01-11 | Mr. Harper | Sunset Boulevard 1100 ...

Table Voucher_Items:
VoucherItemID | VoucherID | ArticleNr | Amount | Price ...
1             |  1        | ponat1    | 1      | 5.00 //(Postage)
2             |  1        | 08343433  | 2      | 3.95 //(Any other article)

没有邮资的订单:

Table Voucher:
VoucherID | VoucherDate | Name       | Address ...
2         |  2017-01-08 | Mr. Marc   | Central Street 33 ...

Table Voucher_Items: //Note the misssing ArticleNr = 'ponat1' article as this order is postage free:

VoucherItemID | VoucherID | ArticleNr | Amount | Price ...
3             | 2         | 00548878  | 3      | 3.95 //(Any other article)

我的问题现在很简单:如何伪造查询以接收所有订单,包括使用

邮资
WHERE ARticleNr = 'ponat1' OR ArticlenNr IS NULL

这里的一个大问题是ArticleNr实际上从不是NULL(因为其他文章总是存在),而是一条带有ArticleNr =' ponat1'如果订单是免邮费的话,根本就不存在,这意味着我的结果中缺少没有邮资的订单。我不想要的。

我希望他们在结果数组中使用0€邮资。

目前我总是要编写一个PHP脚本,或者提取所有订单,然后获取所有order_items并比较哪个订单没有带有nr' ponat1'将邮资设为0非常烦人。

我尝试了LEFT JOIN,RIGHT JOIN,UNION LEFT和RIGHT OUTER JOIN组合,并且没有找到可用的结果。 这甚至可以解决,还是桌面结构对这个问题不好?因为我个人认为凭证 - 邮资相当于1:1的关系,而且应该位于表格凭证中而不是单个字段,但正如我所说,我没有创建这个不幸的结构,我们无法改变,因为我们无法改变使用该表的系统结构

使用单个查询解决此问题会有很大帮助。

3 个答案:

答案 0 :(得分:0)

您正在寻找的内容有点不清楚,因为您没有提供预期输出的任何示例。但是,修改示例查询,可以尝试以下操作:

SELECT ...
FROM Voucher v
LEFT JOIN Voucher_Item vi USING(VoucherID)
AND postage.ArticleNr <> 'ponat1'
LEFT OUTER JOIN Voucher_Item postage
ON postage.VoucherID = v.VoucherID
AND postage.ArticleNr = 'ponat1'
WHERE ...

使用上述结构,您可以将邮资物品与其他物品分开。只需从邮资别名表中选择,检查它是否为空或有价格。

请注意,这将为Voucher_Item表中的每条记录返回一行,如果没有非邮资项,则Voucher_Item数据将为null。如果每个凭证记录只需要一行,则需要包含有关所需信息的更多信息。那时候可能需要GROUP BY。

答案 1 :(得分:0)

您可以做的是添加一个项目(在您的邮件中邮资),该项目已添加到所有优惠券中;

INSERT INTO Voucher_Item VALUES (4, 0, 'ponat1', 0, 0.00);

请注意,此项目VoucherID0

否则

SELECT v.VoucherID, vi.*
  FROM Voucher v LEFT JOIN Voucher_Item vi
    ON v.VoucherID = vi.VoucherID OR vi.VoucherID = 0
  ORDER BY v.VoucherID;

将导致

VoucherID   VoucherItemID   VoucherID   ArticleNr   Amount  Price
1           2               1           08343433    2       3.95
1           4               0           ponat1      0       0
1           1               1           ponat1      1       5
2           3               2           00548878    3       3.95
2           4               0           ponat1      0       0

所以我们几乎就在那里,只有带邮资的优惠券,现在有两次邮资。幸运的是,由于邮资金额和默认邮资的价格为0,我们可以简单地按商品编号分组,并对金额和价格进行总结。

SELECT vi.VoucherItemId, v.VoucherID, vi.ArticleNr,
    SUM(vi.Amount) AS Amount, SUM(vi.Price) AS Price
  FROM Voucher v LEFT JOIN Voucher_Item vi
    ON v.VoucherID = vi.VoucherID OR vi.VoucherID = 0
  GROUP BY v.VoucherID, vi.ArticleNr
  ORDER BY v.VoucherID, vi.VoucherItemID;

哪个结果

VoucherItemID   VoucherID   ArticleNr   Amount  Price
1               1           ponat1      1       5
2               1           08343433    2       3.95
3               2           00548878    3       3.95
4               2           ponat1      0       0

See the fiddle

编辑以澄清现有软件未受影响且现有数据无需更改:

要实施此解决方案,您只需要在Voucher_Item中插入1条记录,该记录将适用于所有凭证(过去和将来创建)。

此外,如果它不会影响当前软件,因为运行现有查询将返回没有添加邮资记录的记录

SELECT vi.*
  FROM Voucher v INNER JOIN Voucher_Item vi
    ON v.VoucherID = vi.VoucherID
  ORDER BY v.VoucherID, vi.VoucherItemID;

仍会返回

VoucherItemID   VoucherID   ArticleNr   Amount  Price
1               1           ponat1      1       5
2               1           08343433    2       3.95
3               2           00548878    3       3.95

答案 2 :(得分:0)

如果我理解正确,您希望在没有邮资行时添加其他(虚拟)行。您可以使用union

完成此操作
SELECT ...
FROM 
  (select v.*, 
     vi.VoucherItemID, vi.ArticelNr, vi.Amount, vi.Price
   FROM Voucher v
   LEFT JOIN Voucher_Item vi -- maybe not a left join?
   on v.VoucherId = vi.VoucherID

   UNION   -- use "union all" if v.* is not unique

   SELECT v.*, 
     null as VoucherItemID, 'ponat1' as ArticleNr, 1 as Amount, 0 as Price
   FROM Voucher v
   WHERE not exists 
    (select 1 from Voucher_Item vi
     where v.VoucherID = vi.VoucherID 
     and vi.ArticleNr = 'ponat1')
  ) as v_compl
WHERE ...

它将接受所有订单,并为每个凭证添加一行,但找不到'ponat1' - 行。 (VoucherID, ArticleNr)上的索引会有所帮助。您可能希望为该子查询创建一个视图,或者按原样使用它。

它不会重新计算VoucherItemID(可能是自动增量字段)。如果您也需要,可以添加另一个图层并计算行号,例如使用负行号作为VoucherItemID或添加足够高的偏移量以使其唯一。

我不确定为什么在“每个订单至少包含一行或多行文章”时使用left join,但为了完整性,请注意此查询还会向有优惠券的凭证添加一行根本没有任何物品。