慢MySQL查询 - JOIN中的CASE

时间:2017-08-29 17:12:13

标签: mysql query-optimization

我有两个表: all_ufo (U)和权利(R)。见ERD: enter image description here

权利是父表。 all_ufo 是孩子。

这是我的疑问:

SELECT *
FROM all_ufo U
LEFT JOIN rights R
ON CASE
  WHEN U.UPC IN (
    SELECT DISTINCT UPC
    FROM rights
    WHERE UPC IS NOT NULL
    AND Channels IS NULL)
  THEN R.UPC 
    AND U.UPC = R.UPC

  WHEN U.Artist IN (
    SELECT DISTINCT Artist
    FROM rights
    WHERE Artist IS NOT NULL
    AND Channels IS NULL)
  THEN R.Artist
    AND U.Artist = R.Artist

  WHEN U.Label IN (
    SELECT DISTINCT Label
    FROM rights
    WHERE Label IS NOT NULL
    AND Channels IS NULL)
  THEN R.Label
    AND U.Label = R.Label
END
;

权利表有点奇怪:它有3个级别的合同,我想将 all_ufo 中的每个订单项与一个且只有一个合同匹配。< / p>

查询在R中为U中的每一行(其中有一行)查找匹配项,从UPC开始,然后是艺术家,然后是标签。

这是R表的一个示例。这里有一行代表表中的每种条目(NULL值在这里显示为字符串&#34; NULL&#34;,但在数据库中它们实际上是null):

这里有来自U的20个随机行的样本:

在我的样本数据(20行)上,我得到了预期的结果。但是当我在整个桌子上运行它(大约600K行)时,它会运行一个小时左右然后终止。

我还尝试将R表分成三个单独的表,每个表对应一个类型。这是我尝试的MySQL脚本,由于SQL(?)不正确而失败:

SELECT *
FROM all_ufo U
CASE
WHEN U.UPC IN (
SELECT DISTINCT UPC
FROM Contracts_Release
WHERE Channels IS NULL)
THEN LEFT JOIN Contracts_Release R
ON (U.UPC = R.UPC
AND R.ContractLevel = 'ReleaseLevel')

WHEN U.Artist IN (
SELECT DISTINCT Artist
FROM Contracts_Artist
WHERE Channels IS NULL)
THEN LEFT JOIN Contracts_Artist R
ON U.Artist = R.Artist
AND R.ContractLevel = 'ArtistLevel'

WHEN U.Label IN (
SELECT DISTINCT Label
FROM Contracts_Label
WHERE Channels IS NULL)
THEN LEFT JOIN Contracts_Label R
ON U.Label = R.Label
AND R.ContractLevel = 'LabelLevel'
END
;

我从未学过很多关于索引,数据库调优,查询优化等的知识。但我尝试了这个没有任何键的查询,两个表都有主键,索引基于各列,索引基于多列。我尝试过的任何内容都无法运行该查询&#34;正确&#34;。

谁能告诉我这里最好的方法?我一直在研究和试验之间交替进行。错误大约5天了...

3 个答案:

答案 0 :(得分:2)

确保在R中仅加入一行的简单方法是向R添加主键列,并引用该主键。然后关于合同级别的所有复杂逻辑都会消失。 U中的一行只能引用R中的一行。

ALTER TABLE rights 
     ADD COLUMN rights_id INT AUTO_INCREMENT,
     ADD PRIMARY KEY (rights_id);
ALTER TABLE all_ufo ADD COLUMN rights_id INT;

将合同级别的详细信息视为R中行的属性,而不是行的标识。也就是说,一旦你将U匹配到R中的正确行,那么你就可以找出它的合同级别。

SELECT ...
FROM all_ufo U
JOIN rights R ON U.rights_id = R.rights_id
WHERE R.Channels IS NULL;

P.S。:你的SQL中还有很多其他东西让人说“WTF?”例如:

    SQL中的
  • CASE只是一个表达式,在每种情况下都不能嵌入JOIN子句和其他东西。它与C ++中的case构造不同。在这里阅读MySQL的IFCASEhttps://dev.mysql.com/doc/refman/5.7/en/control-flow-functions.html#operator_case

  • 为什么几乎每列使用TEXT和BIGINT?有充分的理由选择更适合每列的数据类型。如果您不能很好地了解数据以选择数据类型,那么您可能还没有充分考虑过您的项目。

  • 数量是多少?这意味着您的数量可能不是整数值?似乎不太可能。

  • USD.Payable是DOUBLE?由于舍入行为,您不应该使用FLOAT或DOUBLE作为货币。 If I had a dime for every time I've seen someone use FLOAT to store currency, I'd have $999.997634.

  • 避免使用带有点的列名。 SQL 允许在名称中使用标点符号和特殊字符,但每次使用时都必须分隔列名。如果使用_而不是点,则不必分隔列名。

答案 1 :(得分:1)

对于您必须使用的内容,您可以尝试以下作为UNION。根据自己的绩效标准尝试各自。结果将全部累积到一个结果集中。基于UPC是主要的合格查询,然后是艺术家和最后的标签。

现在,如果给定记录符合多个或所有部分,并且您只希望它显示为ONCE,您可能只需应用&#34;选择DISTINCT&#34;在主循环中,以防止重复从艺术家拉入并标记UNION部分。

SELECT *
   FROM all_ufo U
   JOIN rights R
      ON U.UPC = R.UPC
      AND R.UPC IS NOT NULL
      AND R.Channels IS NOT NULL
UNION
SELECT *
   FROM all_ufo U
   JOIN rights R
     ON U.Artist = R.Artist
     AND R.Artist IS NOT NULL
     AND R.Channels IS NULL
UNION
SELECT *
   FROM all_ufo U
   JOIN rights R
     ON U.Label = R.Label
     AND R.Label IS NOT NULL
     AND R.Channels IS NULL

答案 2 :(得分:1)

在我看来,您的rights表格是艺术家各种付款条款的联合体。看起来你正试图获得每个曲目的列表。

看起来发布的曲目是通过条形码(UPC)上的匹配来支付的。其他种类由艺术家或标签支付。

这是一种完成该任务的方法,无需长达一小时的研磨,然后超时。 (http://sqlfiddle.com/#!9/047a1b/4/0

SELECT U.*,
       COALESCE(rel.ContractLevel, artist.ContractLevel, label.ContractLevel) ContractLevel,
       COALESCE(rel.Payee, artist.Payee, label.Payee) Payee,
       COALESCE(rel.Rate_percent, artist.Rate_percent, label.Rate_percent) Rate_percent,
       COALESCE(rel.startdate, artist.startdate, label.startdate) startdate,
       COALESCE(rel.expirationdate, artist.expirationdate, label.expirationdate) expirationdate

FROM all_ufo U
LEFT JOIN rights rel ON U.UPC = rel.UPC AND rel.ContractLevel = 'ReleaseLevel' 
LEFT JOIN rights artist ON  U.Artist = artist.Artist   AND artist.ContractLevel='ArtistLevel'
LEFT JOIN rights label ON  U.Label = label.Label   AND label.ContractLevel='LabelLevel'

它在多个rights操作中重用LEFT JOIN表。然后,它使用COALESCE()函数为每一行查找相关的非空值(对ON子句有命中的值)。