SQL OR 返回这两个表达式



我有一个存储的SQL过程,我想根据COALESCE或OR的结果加入另一个表,但不知何故,似乎合并返回了两个表达式。

用例:如果播放列表不存在时间表 ->返回默认时间表 (24/7)。这就像一个魅力,我得到ID 1的时间表。但是,如果有计划,则过程将返回两个计划。:-(

这是我的代码:

...
RIGHT JOIN schedule
ON COALESCE(playlists.ID = schedule.PLID,playlists.SCHEDULES_ID=1)
WHERE devices.IDENTIFIER = ParamID;

有什么建议吗?

提前感谢!

彼得

更新

似乎合并不是我的情况的正确解决方案。有人有建议吗?

我有一个表"时间表",其中有一列"PLID"(播放列表ID)。在我的查询中,我想根据此值连接此表。如果没有具有匹配 PLID 的计划,我想加入默认计划 (ID = 1)。

更新 2

我现在正在尝试一种新的方法,但似乎仍然不起作用。如果没有时间表,我得到默认时间表(ID1)。如果有计划 ->我得到默认值 + 实际分配的计划 (PLID = playlists.ID)

LEFT JOIN schedule
ON (schedule.PLID=playlists.ID) 
OR (schedule.ID = '1')

作为联接出错的原因是,您已经安排了联接,以便联接在联接到默认值时始终成功,有时在联接到数据时也会成功。 但至关重要的是,对默认数据的联接会导致与实际数据联接不同的行,因此它们不再是相关的行

真的很难告诉你为什么你的查询失败,因为你没有包含足够的示例数据,但我会尝试发明一些

playlists.ID  playlists.schedulesID  playlist.name
1             1                      default playlist schedule holder
2             100
3             null
schedule.ID  schedule.PLID  schedule.name
X            1              default 247 schedule                   
Y            2            custom schedule 100
Z            null           nully schedule

COALESCE(playlists.ID=schedule.PLID,playlists.SCHEDULES_ID=1)
1,X  true on 1st argument, row shows
1,Y  false on 1, row hides
1,Z  null on 1, true on 2, row shows
2,X  false on 1, row hides
2,Y  true on 1, row shows
2,Z  null on 1, false on 2, row hides
3,X  false on 1, false on 2, row hides
3,Y  false on 1, false on 2, row hides 
3,Z null on 1, null on 2, row hides

请注意为第一个设置的真值 - 两行有一个真值,因此 1,X 和 1,Z 显示给出"重复"行 1

将布尔表达式放入您的 COALESCE 中可能是您可能只能在 mysql 上侥幸逃脱的事情,也许是 Postgres。Oracle和sqlserver不允许这样做。不过,这并不是问题的确切原因;提供默认值的逻辑被破坏,因为它不会尝试将默认值放在与行相同的行上,以防缺少该行

你最好做这样的事情:

SELECT 
COALESCE(a.x, d.x) as x,
COALESCE(a.y, d.y) as y
FROM
tableWithAllRows a
LEFT JOIN
tableWithSomeRows s ON a.id = s.id
CROSS JOIN
(
SELECT * FROM sometable WHERE ID = -1 --values in row is -1 are defaults
) d

请注意,这里我们专门使用外部进行连接,以便我们容忍丢失的数据,然后我们使用始终成功的交叉连接并确保子查询只有一行。这意味着外部联接中的每一行都有一个可用的默认行。我们使用合并来拉取默认值,而不是进行连接。

编辑;你似乎表明你不明白这一点,所以我会举另一个更容易理解的关系的例子

在家里,你有食物储存。有时储藏室是橱柜,有时是冰箱。食品可以具有定义的存储位置或默认存储位置:

ID, Name 
1, Cupboard
2, Refrigerator
Food, Name, StorageID
A, Milk, 2
B, Cereal, 1
C, Beer, null

牛奶放在冰箱里,麦片放在橱柜里,啤酒没有指定(我们将默认它)。你对此很好:

SELECT * FROM
storage s
LEFT JOIN food f ON f.StorageID = s.ID

啤酒的存储结果为空,因为它没有设置存储 ID。您决定默认将其放入橱柜:

SELECT * FROM
storage s
LEFT JOIN food f 
ON 
f.StorageID = s.ID OR
s.ID = 1 --default to cupboard

这对谷物非常有用,因为 StorageID = ID,哦,而且 s.ID=1 也是真的,但你不能有"更真实" - OR 只是真的或真的,这两个条件对于谷物都是真的,所以谷物来一次

B,Cereal,1,1,Cupboard

啤酒也很好。StorageID 为空,因此 StorageID = ID 不为 true,但 ID=1 为真:

C,Beer,null,1,Cupboard

目前为止,一切都好!但这一切都随着牛奶而分崩离析:

A,Milk,2,2,Refrigerator --Milk goes in the fridge because StorageID=ID
A,Milk,2,1,Cupboard     --oh no

出现此 Oh No 行是因为您默认说ID=1- 您可以看到这是真的!那一行确实有id1,橱柜在里面。我们的 ON 子句没有任何内容阻止它出现。也许我们可以更改查询:

SELECT * FROM
storage s
LEFT JOIN food f 
ON 
f.StorageID = s.ID OR
(f.storageID IS NULL AND s.ID = 1) --default to cupboard if no storage id 

它将工作,直到您达到:

D,Wine,3

什么是存储 ID 3?不知道;它不在表中,但左连接带来了它。3 也不是 null,因此该行最终看起来像:

D,Wine,3,null,null

现在我们陷入了困境。没有办法解决这个问题,因为您之前引入默认值的尝试确实为啤酒带来了默认值,但它为谷物带来了额外的值,即第二行(与第一行无关)。我们试图通过向我们的ON添加更多东西来压制它,但无论我们做什么,都会出错

在某些情况下,我们不需要使用 OR 来为我们提供更多的行,我们需要另一个 JOIN 来为我们提供更多列,并且我们将确保这些列具有在实际数据不存在时我们选择作为默认值的值:

SELECT * FROM
storage s
LEFT JOIN food f 
ON 
f.StorageID = s.ID
CROSS JOIN 
(SELECT * FROM storage WHERE ID = 1) def

了解我们如何添加另一个联接,然后从存储表(橱柜行)中拉出一行。交叉连接它意味着它将在每一行上重复。

数据现在如下所示:

A,Milk,2,2,Refrigerator,2,Cupboard
B,Cereal,1,1,Cupboard,2,Cupboard
C,Beer,null,null,null,2,Cupboard
D,Wine,3,null,null,2,Cupboard

看到最后两列中的默认值了吗?现在,我们可以使用 mergeesce 尝试先获取分配的位置,然后获取默认位置:

SELECT COALESCE(s.Name,def.Name)

对于牛奶,这返回冰箱。对于啤酒,它首先找到 null,因此它转到默认值并返回该值。

然后你意识到啤酒和葡萄酒最好放在冰箱里,因为毕竟你需要一个很酷的。将子查询更改为"其中 ID = 2">


执行此操作的另一种复杂方法是使用一对子查询,一个为匹配的行提供实际值,另一个为不匹配的行提供默认值,将它们联合在一起,然后将数据内部联接到它们。Inner 现在可以工作了,因为我们确保右侧的表对左侧的每一行都有一行

第三种方法是对行进行编号,其中特定的行优先于默认行(较低的行号),然后仅采用"优先级 1"行

顺便说一下,您的计划表与一组列上的播放列表相关,而播放列表与另一组列上的计划相关。这可能是一个非常糟糕的主意,会让你

长时间打结 确定是单个播放列表具有多个计划,还是单个计划具有多个播放列表。如果多个播放列表有多个计划,请将其分解为两个 1:M 关系,并带有播放列表计划表

我想你想要这个逻辑:

...
RIGHT JOIN schedule
ON playlists.ID = schedule.PLID OR
playlists.SCHEDULES_ID = 1
WHERE devices.IDENTIFIER = ParamID;

如果 ID 相等,或者如果不是,则播放列表的计划 ID 为 1,这将发生联接。

最新更新