0

我对变量在 mysql 中的工作方式有疑问。正如我在他们的网站上看到的那样,设置一个变量将对下一行可见。

我的桌子是这样的:

A     B     C      N
1    NULL   NULL   4
1    NULL   NULL   4 
1     1     NULL   4 
1     1     NULL   4
1     1     1      4
1     1     1      4

我想要的是只返回带有C = 1. 如果没有行,则返回B = 1 and C is NULL如果没有行A = 1 and B is NULL and C is NULL

我的想法是:

select N as number,
       @var_c := case when (C = 1) then 1 else -1 end as myc,
       @var_b := case when (@var_c < 0 and B = 1) then 1 else -1 end as myB,
       @var_c := case when (@var_a < 0 and var_b < 0 and C = 1) then 1 else -1 end as myC

from (select @var_a := -1) r_a,
     (select @var_b := -1) r_b,
     (select @var_c := -1) r_c,
     (select A, B, C, N from my_table order by A desc, B desc, C desc) rows

它应该(我想)返回

number     myA    myB    myC
  4         -1      -1    1
  4         -1      -1    1
  4         -1      -1    -1
  4         -1      -1    -1
  4         -1      -1    -1
  4         -1      -1    -1

有了这个,having myA > 0 or myB > 0 or myC > 0就可以了。

但它正在回归

number    myA     myB    myC
  4         1      -1    -1
  4         1      -1    -1
  4         -1      -1    1
  4         -1      -1    1
  4         -1      1    -1
  4         -1      1    -1

Mysql 不应该跨行保留变量吗?

问候。

4

2 回答 2

1

首先,不要使用这样的变量。您不应该期望您可以在FROM子句中设置变量然后访问SELECT子句中的值。这不是变量的工作方式。通常你应该只在查询的一个地方使用一个变量,否则你会得到不确定的结果。

其次,你为什么不发出三个不同的查询呢?首先 for A = 1 and B is NULL and C is NULL,如果它不返回任何行,则发出带有第二个条件集的查询。等等。

如果你最终只想发出一个查询,你可以试试这个:

SELECT N as number
FROM my_table
WHERE IF(EXISTS (SELECT A FROM my_table WHERE A = 1 and B is NULL and C is NULL),
    A = 1 and B is NULL and C is NULL,
    IF(EXISTS (SELECT A FROM my_table WHERE B = 1 and C is NULL), B = 1 and C is NULL, C = 1))

但这很可能会影响性能。所以最好只使用三个查询而不是一个。

UPD:还有另一种(但类似的)方法:

SELECT N as number
FROM my_table
WHERE (
  A = 1
  AND B is NULL
  AND C is NULL
) OR (
  B = 1
  AND C is NULL
  AND NOT EXISTS (SELECT A FROM my_table WHERE A = 1 and B is NULL and C is NULL)
) OR (
  C = 1
  AND NOT EXISTS (SELECT A FROM my_table WHERE A = 1 and B is NULL and C is NULL)
  AND NOT EXISTS (SELECT A FROM my_table WHERE B = 1 and C is NULL)
)
于 2012-04-26T15:36:58.927 回答
0

从另一个示例及其评论中,我会以这种方式处理您的查询。如果有任何“C = 1”值并存储到 CCnt 字段中,则第一个查询进行资格预审。如果未找到,则强制 CCnt 值为 NULL。第二,对“B”列做同样的事情。由于这些中的每一个的限制为 1,只要它可以分别找到 C=1 或 B=1 的条目,它应该很快。如果不是,它们的值为 NULL。为什么要这样做?每次运行查询都会完成一次查询并完成答案。它不必重复执行查询,因为当用作子查询时可能会降低性能。执行不带 group by 的 count() 将 GUARANTEE 作为单行,因此您不会有任何可能导致重复的笛卡尔结果。

现在这个抢先查询已经完成,现在添加你的主表。此时,“HasCEntry.CCnt”值将是 1 或 NULL。“HasBEntry.CCnt”值将是 1 或 NULL。

select 
      YT.*
   from
      ( select IF( count(*) > 0, 1, NULL ) as CCnt
           from YourTable
           where C = 1
           limit 1 ) HasCEntry,
      ( select IF( count(*) > 0, 1, NULL ) as BCnt
           from YourTable
           where B = 1
           limit 1 ) HasBEntry,
      YourTable YT
   where
            YT.C = 1
      OR  ( YT.B = 1 AND HasCEntry.CCnt IS NULL )
      OR  ( YT.A = 1 AND HasBEntry.BCnt IS NULL AND HasCEntry.CCnt IS NULL )

现在,遵循 WHERE 子句。如果甚至有 1 条 C=1 的记录,那么 YT.C = 1 就是您所需要的并且它已经完成了......以及您不关心的其他“OR”条件,记录就会出现。

现在,假设没有 C = 1 的条目。我们知道“CCnt”值为 NULL,因此将进入第一个“OR”条件并针对“B”进行测试...... B 具体 = 1 并且 CCnt 是NULL... 因此,如果有一个“C=2”条目(3、4 或任何其他值),我们不想要它。我们只想要 B = 1 AND C = NULL ...

如果 C 和 B 失败,那么我们就落入“A”测试。同样,A = 1 AND B 为空且 C 为 NULL,原因与上述 B 相同。

对于一张小桌子,您可能不会注意到与 Shedal 的答案有任何显着的性能差异。但是,随着您的桌子的增长,它可能会成为一个巨大的打击。

于 2012-04-28T21:44:19.367 回答