【问题标题】:difficulty with mysql join (nulls not showing)mysql 连接困难(未显示空值)
【发布时间】:2014-08-17 16:11:45
【问题描述】:

所以我有 6 张桌子,每张桌子都有 2 列。 3 个有我正在使用的数据,另外 3 个只是指定它们之间的关系。

所以用这个示例数据:

central_item

id   name
-----------------
1    Chicken
2    Shrimp


cooking_method

id   name
-----------------
1    Bake
2    Fry


style

id   name
-----------------
1    Casserole
2    Pie


central_item_cooking_method

central_item  cooking_method
------------------------------
1             1
1             2
2             1
2             2


central_item_style

central_item  style
------------------------------
1             1
1             2
2             1


cooking_method_style

cooking_method  style
------------------------------
1               1
2               2

我正在努力解决这个问题:

central_item_name  style_name  cooking_method_name
----------------------------------------------------
Chicken            null        Bake
Chicken            null        Fry
Chicken            Casserole   null
Chicken            Casserole   Bake
Chicken            Pie         null
Chicken            Pie         Bake
Shrimp             null        Bake
Shrimp             null        Fry
Shrimp             Casserole   null
Shrimp             Casserole   Bake

这是我一直在尝试的查询。 cmets 解释了每个部分应该做什么。当我运行它时,查询会丢失很多我希望看到只有 1 列为空的结果。

SELECT
#name these something better so they don't all get returned as just 'name'
central_item.name as `central_item_name`, style.name as `style_name`, cooking_method.name as `cooking_method_name`
#we need a central item no matter what so start here
FROM central_item
#get styles for items (optional)
LEFT JOIN central_item_style ON central_item_style.style = central_item.id OR central_item_style.style IS NULL
#get names for any matching styles
LEFT JOIN style ON style.id = central_item_style.style OR style.id IS NULL
#get cooking methods for items (optional)
LEFT JOIN central_item_cooking_method ON central_item_cooking_method.central_item = central_item.id OR central_item_cooking_method.central_item IS NULL
#get names for cooking methods
LEFT JOIN cooking_method ON cooking_method.id = central_item_cooking_method.cooking_method OR cooking_method.id IS NULL
#for the matching item cooking methods check which styles also match the cooking method.  For item styles check for matching cooking methods.
LEFT JOIN cooking_method_style ON cooking_method_style.style = central_item_style.style OR cooking_method_style.cooking_method = central_item_cooking_method.cooking_method

WHERE
#make sure we have at least one of these
(style.id IS NOT NULL OR cooking_method.id IS NOT NULL) AND
#cooking method or style can be null but we have both they need to be compatible with each other
(cooking_method.id IS NULL OR central_item_style.style = cooking_method_style.style) AND
(style.id IS NULL OR central_item_cooking_method.cooking_method = cooking_method_style.cooking_method)

#Remove duplicates
GROUP BY central_item.name, style.name, cooking_method.name
ORDER BY central_item.name, style.name, cooking_method.name

【问题讨论】:

  • 这是一个垃圾例子。谁炸意大利面?
  • 那里。我把它改成了虾。其余的仍然有意义。
  • 好。一个项目有点像一种成分,不是吗?
  • 您确定这是正确的结果集吗?
  • 查看我的答案。这是正确的结果集。例如,您可以吃烤鸡。或者烤鸡砂锅。它们是 2 个不同的结果。

标签: mysql database join left-join


【解决方案1】:

考虑这个示例(但请注意,它返回的结果集略有不同)...

 DROP TABLE IF EXISTS ingredients;
 CREATE TABLE ingredients
 (ingredient_id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
 ,ingredient VARCHAR(12) NOT NULL
 );

 INSERT INTO ingredients VALUES (1    ,'Chicken'),(2    ,'Shrimp');

 DROP TABLE IF EXISTS methods;
 CREATE TABLE methods
 (method_id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
 ,method VARCHAR(12) NOT NULL
 );

 INSERT INTO methods VALUES
 (1    ,'Bake'),
 (2    ,'Fry');

 DROP TABLE IF EXISTS styles;
 CREATE TABLE styles
 (style_id   INT NOT NULL
 ,style VARCHAR(12) NOT NULL
 );

 INSERT INTO styles VALUES
 (1    ,'Casserole'),(2    ,'Pie');

 DROP TABLE IF EXISTS ingredient_method;
 CREATE TABLE ingredient_method
 (ingredient_id INT NOT NULL,method_id INT NOT NULL,PRIMARY KEY(ingredient_id,method_id));

 INSERT INTO ingredient_method VALUES
 (1             ,1),(1             ,2),(2             ,1),(2             ,2);

 DROP TABLE IF EXISTS ingredient_style;
 CREATE TABLE ingredient_style
 (ingredient_id INT NOT NULL,style_id INT NOT NULL,PRIMARY KEY(ingredient_id,style_id));

 INSERT INTO ingredient_style VALUES
 (1             ,1),(1             ,2),(2             ,1);


 DROP TABLE IF EXISTS method_style;

 CREATE TABLE method_style
 (method_id INT NOT NULL,style_id INT NOT NULL,PRIMARY KEY(method_id,style_id));

 INSERT INTO method_style VALUES(1               ,1),(2               ,2);


 SELECT i.ingredient
      , s.style
      , m.method
   FROM ingredients i
   LEFT
   JOIN ingredient_style si
     ON si.ingredient_id = i.ingredient_id
   LEFT
   JOIN ingredient_method im
     ON im.ingredient_id = i.ingredient_id   
   LEFT
   JOIN styles s
     ON s.style_id = si.style_id
   LEFT
   JOIN method_style ms
     ON ms.style_id = si.style_id
   LEFT
   JOIN methods m 
     ON m.method_id IN(ms.method_id,m.method_id = im.method_id);

+------------+-----------+--------+
| ingredient | style     | method |
+------------+-----------+--------+
| Chicken    | Casserole | Bake   |
| Chicken    | Casserole | Bake   |
| Chicken    | Casserole | Fry    |
| Chicken    | Pie       | Bake   |
| Chicken    | Pie       | Fry    |
| Chicken    | Pie       | Fry    |
| Shrimp     | Casserole | Bake   |
| Shrimp     | Casserole | Bake   |
| Shrimp     | Casserole | Fry    |
+------------+-----------+--------+

http://sqlfiddle.com/#!2/b0127/1

【讨论】:

    【解决方案2】:

    我最终使用的解决方案是为名为“none”的样式添加一个值(例如,鸡肉可以只是鸡肉,但无论你如何烹饪,你都永远不会只吃碎牛肉。实际价值有道理)。一旦我这样做了,就不需要显示空值了。这样做会使这种方式变得更容易。

    SELECT central_item.name as central_item_name, cooking_method.name as cooking_method_name, style.name as style_name FROM central_item
    INNER JOIN central_item_style ON central_item_style.central_item = central_item.id
    INNER JOIN style ON style.id = central_item_style.style
    INNER JOIN cooking_method_style ON cooking_method_style.style = central_item_style.style
    INNER JOIN cooking_method ON cooking_method.id = cooking_method_style.cooking_method
    INNER JOIN central_item_cooking_method ON central_item_cooking_method.cooking_method = cooking_method_style.cooking_method AND central_item_cooking_method.central_item = central_item.id
    GROUP BY central_item_name, cooking_method_name, style_name
    ORDER BY central_item_name, cooking_method_name, style_name
    

    如果其他人需要,我提出的原始问题的解决方案就是这个。可能有更好的方法,但这是我能想到的最好方法。

    它基本上联合了一个查询 central_item/cooking_method 对、central_item/style 对,最后是 central_item/cooking_method/style 关系。然后我必须将整个事情包装在另一个查询中,因为它会生成很多垃圾结果(例如 style_name 和 cook_method_name 对于许多结果都是空的)。

    我确信有更好的方法来做到这一点,但它超出了我的能力。

    SELECT central_item_name, cooking_method_name, style_name FROM
    (SELECT central_item.name as central_item_name, null as cooking_method_name, null as style_name FROM central_item
    
    UNION
    
    SELECT central_item.name as central_item_name, cooking_method.name as cooking_method_name, null as style_name FROM cooking_method
    INNER JOIN central_item_cooking_method ON central_item_cooking_method.cooking_method = cooking_method.id
    INNER JOIN central_item ON central_item.id = central_item_cooking_method.central_item
    
    UNION
    
    SELECT central_item.name as central_item_name, null as cooking_method_name, style.name as style_name FROM style
    INNER JOIN central_item_style ON central_item_style.style = style.id
    INNER JOIN central_item ON central_item.id = central_item_style.central_item
    
    UNION
    
    SELECT central_item.name as central_item_name, cooking_method.name as cooking_method_name, style.name as style_name FROM central_item
    INNER JOIN central_item_style ON central_item_style.central_item = central_item.id
    INNER JOIN style ON style.id = central_item_style.style
    INNER JOIN cooking_method_style ON cooking_method_style.style = central_item_style.style
    INNER JOIN cooking_method ON cooking_method.id = cooking_method_style.cooking_method
    INNER JOIN central_item_cooking_method ON central_item_cooking_method.cooking_method = cooking_method_style.cooking_method AND central_item_cooking_method.central_item = central_item.id
    
    
    ORDER by central_item_name, cooking_method_name, style_name) iq
    WHERE iq.cooking_method_name IS NOT NULL or iq.style_name IS NOT NULL
    

    【讨论】:

    • 鞑靼牛排,有人吗?
    • @Mark 你能解释一下为什么在这个查询中需要cooking_method_style 表吗?因为我能想象的唯一原因是消除质量差数据中的错误。但是当您的数据处于良好状态时,我认为使用它没有任何意义。
    • 我最终重新设计了这个,因为你基本上是对的。现在第一个关系是中心项目、烹饪方法和风格之间的关系。然后我只看风格,找到相关的烹饪方法。例如,中心项目:牛排,cooking_method:烧烤,风格:比萨,cooking_method(用于比萨):烘焙。所以我仍然需要 Cooking_method_style 来关联披萨和烹饪方法,但我最初的做法是错误的。可能是更好的例子,但就是这样。
    【解决方案3】:
    select 
        central_item.name central_item_name, 
        cooking_method.name cooking_method_name, 
        style.name style_name
    from
        central_item ci
        left join (
            central_item_style cis
            join style s on cis.style = s.id
        ) on ci.id = cis.central_item
        left join (
            central_item_cooking_method cicm
            join cooking_method cm on cicm.cooking_method = cm.id
        ) on ci.id = cicm.central_item
    

    如果您的数据库存在数据完整性问题,可选代码行:

        left join cooking_method_style cms on 
            cms.style = cis.style and cms.cooking_method = cicm.cooking_method
    where
        cms.style is not null
        or
        cis.style is null or cicm.cooking_method is null
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-10-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-06-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多