仅当存在非空值时,SQL左联接非空值

时间:2020-10-28 22:57:21

标签: sql subquery left-join

我正在使用GEO Location变量,我认为这应该比我做的要简单得多。

我想返回一行,并将其他三个“选项”作为其他值保留在该行上,这是通过使用用户的地理位置来完成的,其中一些选项会有所不同。所有选项都有默认值,因此,如果我与地理位置匹配,我只想覆盖默认值

表1中将包含汉堡,热狗等物品,而表2中将具有浇头之类的东西,并且每个表项最多具有三层浇头,其中一些会根据地理位置而改变。

表一

   |id | name   |
   ______________
   |0  | burger |
   |1  | hotdog |

表二

   |topid | topping | layer | location
   ___________________________________
   | 0    | catsup  | 1     | 
   | 1    | mustard | 2     | 
   | 2    | onion   | 3     | 
   | 3    | lettuce | 3     | US
   | 4    | bacon   | 3     | CA


$geo = 'CA';

mySql

SELECT item.name, layer1.topping as `layer1`, layer2.topping as `layer2`, layer3.topping as `layer3` 
FROM `one` item
LEFT JOIN `two` layer1 
ON layer1.layer = 1
AND layer1.location IS NULL OR layer1.location = $geo
LEFT JOIN `two` layer2 
ON layer2.layer = 2
AND layer2.location IS NULL OR layer2.location = $geo
LEFT JOIN `two` layer3 
ON layer3.layer = 3
AND layer3.location IS NULL OR layer3.location = $geo
WHERE item.id = 0

我的结果将是只返回一个带有catsup,芥末酱和培根的汉堡,因此我需要一个如果有NULL值和字母值的字母值作为图层,否则取NULL。我希望做到这一点而不必在查询中嵌套选择以加快返回500条以上记录的速度

1 个答案:

答案 0 :(得分:1)

查询中存在一个问题,其中OR条件的JOIN版部分应放在括号中以获得所需的结果,即条件应如下所示:

ON layer1.layer = 1
AND (layer1.location IS NULL OR layer1.location = $geo)

此问题解决后,您的查询将产生多行,具体取决于two中与$geo匹配的行的存在。您可以根据每一层的位置是否为NULL来对这些行进行排序,从而使匹配最多位置的行成为第一行。然后,您可以使用LIMIT 1仅选择该行:

SELECT item.name, layer1.topping as `layer1`, layer2.topping as `layer2`, layer3.topping as `layer3` 
FROM `one` item
LEFT JOIN `two` layer1 
ON layer1.layer = 1
AND (layer1.location IS NULL OR layer1.location = '$geo')
LEFT JOIN `two` layer2 
ON layer2.layer = 2
AND (layer2.location IS NULL OR layer2.location = '$geo')
LEFT JOIN `two` layer3 
ON layer3.layer = 3
AND (layer3.location IS NULL OR layer3.location = '$geo')
WHERE item.id = 0
ORDER BY layer1.location IS NULL,
         layer2.location IS NULL,
         layer3.location IS NULL
LIMIT 1

输出(对于您的示例数据,假设$geo = 'CA'):

name    layer1  layer2      layer3
burger  catsup  mustard     bacon

Demo on db-fiddle

如果您需要获取所有item.id值,则 需要使用嵌套的SELECT。这是可以完成的一种方法:

SELECT item.name, layer1.topping as `layer1`, layer2.topping as `layer2`, layer3.topping as `layer3` 
FROM `one` item
LEFT JOIN `two` layer1 
ON layer1.layer = 1
AND (layer1.location IS NULL OR layer1.location = 'CA')
LEFT JOIN `two` layer2 
ON layer2.layer = 2
AND (layer2.location IS NULL OR layer2.location = 'CA')
LEFT JOIN `two` layer3 
ON layer3.layer = 3
AND (layer3.location IS NULL OR layer3.location = 'CA')
JOIN(
SELECT item.name,
       MIN((layer1.location IS NULL) + (layer2.location IS NULL) + (layer3.location IS NULL)) AS mostspecific
FROM `one` item
LEFT JOIN `two` layer1
ON layer1.layer = 1
AND (layer1.location IS NULL OR layer1.location = 'CA')
LEFT JOIN `two` layer2 
ON layer2.layer = 2
AND (layer2.location IS NULL OR layer2.location = 'CA')
LEFT JOIN `two` layer3 
ON layer3.layer = 3
AND (layer3.location IS NULL OR layer3.location = 'CA')
GROUP BY item.name
) sp ON item.name = sp.name AND (layer1.location IS NULL) + (layer2.location IS NULL) + (layer3.location IS NULL) = sp.mostspecific

输出:

name    layer1  layer2  layer3
burger  catsup  cheese  bacon
hotdog  catsup  cheese  bacon

Demo on db-fiddle

注释$geo应该用引号引起来,否则将被视为列名。而且,正如戈登在评论中指出的那样,表two似乎应该有一个itemid,以便您可以将浇头链接到特定的项目(以防您将培根作为菜单项)。有效的热狗浇头)。