6

我有 2 张桌子:

CREATE TABLE plans
    (id int, benefit varchar(5), clip_state int);

INSERT INTO plans
    (id, benefit, clip_state)
VALUES
    (1, 'A', 1),
    (2, 'A', 0),
    (3, 'B', 0),
    (4, 'C', 0);

CREATE TABLE clip_states
    (state varchar(2), clip_state int);

INSERT INTO clip_states
    (state, clip_state)
VALUES
    ('LA', 1),
    ('FL', 0);

请注意clip_state是 0 或 1 并且数据模型允许从 2 信息中查询表中的一个或没有计划: & 。首先,使用条件,我们最多可以将表过滤为 2 行,1 行= 0,1 行 = 1。然后通过使用和连接表,我们可以通过检查将结果减少到一行(或零):plansbenefitstatebenefitplansclip_statestateclip_states

  • 如果stateclip_states表中,请确保clip_state在 2 个表之间匹配。如果不匹配,则不返回任何结果。
  • 如果state中不存在clip_states,则仅与plans表中clip_state= 0 的行匹配。

这是我的查询,可以解决问题:

SELECT id, p.clip_state, benefit
FROM plans p
LEFT JOIN clip_states cs ON STATE IN ('LA')
WHERE benefit = 'A' AND
(p.clip_state = cs.clip_state OR (p.clip_state = 0 AND cs.clip_state IS NULL));

如您所见,左连接很奇怪,因为它没有连接两个表之间的关系。所以,我的问题是:

  • 有这样的加盟是正常的吗?
  • 有没有更好的解决方案(清洁和性能)?

您可以在以下位置查看我的解决方案:http ://sqlfiddle.com/#!1/8912d/53

更新1:我已经更新了上面问题的文字。

更新 2:更多信息

  1. 如果 astate不在clip_states表中,则其clip_state隐式等于 0。否则,它clip_state在表中。
  2. 从两个给定的 info:benefit和中,在table where = of that givenstate中找到一行。当然,如果不匹配,则不会返回任何行。plansplans.clip_stateclip_statestate
4

5 回答 5

1

更新问题的更新答案:

SELECT p.*
FROM   plans p
WHERE  p.benefit = 'A'
AND    NOT  EXISTS (
  SELECT 1
  FROM   clip_states cs
  WHERE  cs.state = 'LA'
  AND    cs.clip_state <> p.clip_state
  )
ORDER  BY p.clip_state
LIMIT  1;

-> sqlfiddle

这可能是定义:

  1. plans查找表中给定的所有行benefit

  2. 消除与给定clip_state的表中的同一列不匹配的行。clip_statesstate

  3. 从剩余的行中,返回 中具有最低值的行clip_state

另外,由于

clip_state是 0 或 1

您应该使用数据类型boolean而不是int.

于 2013-01-29T12:39:43.673 回答
1

完全不同的方法:

  1. 如果该状态尚未在表中,则使用 0将所需状态“添加”到clip_states表中。clip_state

  2. 将结果集内连接到planson clip_state,另外过滤 on state

这可能是这样的:

SELECT p.*
FROM plans p
INNER JOIN (
  SELECT state, clip_state FROM clip_states
  UNION ALL
  (
  SELECT 'LA', 0
  EXCEPT
  SELECT state, 0 FROM clip_states
  )
) cs
ON p.clip_state = cs.clip_state
WHERE p.benefit = 'A'
  AND cs.state = 'LA'
;

这是这个查询“在行动”:http ://sqlfiddle.com/#!1/b0feb/7

似乎符合您的要求,除非我再次错过了什么。

于 2013-01-29T19:37:18.200 回答
0

的确,LEFT JOIN这里看起来很奇怪。

  1. plans我看不到和clip_states表之间的连接条件;
  2. 您不使用clip_stateSELECT 列表中的任何列,因此您无法获得NULL目标值。

我建议按照以下方式进行(也在SQL-Fiddle上):

SELECT id, p.benefit, coalesce(cs.clip_state, 0) clip_state
FROM plans p
LEFT JOIN clip_states cs ON cs.clip_state=p.clip_state AND cs.state IN ('LA')
WHERE p.benefit = 'A';
  1. 我已经将连接条件从查询部分移到并简化了它WHEREFROM
  2. 我已将clip_states表中的列添加到 SELECT 列表中,NULL以防万一没有匹配的行;
  3. cs.clip_state什么时候NULL,我把它变成0,正如预期的那样。

我认为这可以满足您的需求。

请看一下这个关于连接的视觉解释,我觉得它非常有用。

于 2013-01-29T08:11:49.193 回答
0

考虑到它可以解决问题:

  • 有这样的加盟是正常的吗?

这不是通常的做法。但在我看来,JOIN 只是在 ON 之后接受一个条件,它可以是任何条件。

  • 有没有更好的解决方案(清洁和性能)?

它看起来是这里可用的最短解决方案,我不明白为什么它会损害性能。

如果您从此查询创建 VIEW 并在选定字段中包含 STATE 字段,则可以从该视图进行查询,如下所示:

SELECT * FROM stateview WHERE state='FL'

顺便说一句,巧合的是我猜,vyegorov提供的解决方案正是我在sql-fiddle中输入的做一些测试。但认为它不正确,所以在那之后的某个时候将其删除。

于 2013-01-29T10:03:18.053 回答
0

我想我必须使用以下查询作为解决方案。我检查了查询计划,发现它比我在问题中的查询好一步。这个想法是首先缩小计划,然后加入 clip_states 表。

SELECT *
FROM (
    SELECT DISTINCT id, clip_state, benefit
    FROM plans
    WHERE benefit = 'A') p
LEFT JOIN clip_states cs ON state IN ('LA')
WHERE (p.clip_state = cs.clip_state OR (p.clip_state = 0 AND cs.clip_state IS NULL));

谢谢大家。

于 2013-01-30T20:40:48.203 回答