除了使用SELECT
或UNION
之外,还有其他方法可以在MySQL查询中合并超过2个UNION ALL
语句吗?我已尝试使用UNION
和UNION ALL
但我的查询加载速度太慢。这是我的问题:
SELECT
'AVAILABLE' AS STATUS,
count(id_status) as BIL
FROM
book_records AS b, book_class AS c
WHERE
b.id_book = c.id_book AND
id_status IN ( 1 ) AND class_desc = 'NOVEL'
UNION
SELECT
'WAITING' AS STATUS,
count(id_status) as BIL
FROM
book_records AS b, book_class AS c
WHERE
b.id_book = c.id_book AND
id_status IN ( 2,3,5 ) AND
class_desc = 'NOVEL'
UNION
SELECT
'DAMAGED' AS STATUS,
count(id_status) as BIL
FROM
book_records AS b, book_class AS c
WHERE
b.id_book = c.id_book AND
id_status NOT IN ( 1,2,3,5 ) AND
class_desc = 'NOVEL'
结果:
STATUS BIL
----------------
AVAILABLE 5
WAITING 25
DAMAGED 0
有谁能告诉我如何解决问题?
答案 0 :(得分:1)
你可以通过一次通过表来获得结果,而不是三遍。
我个人的偏好是返回一个略有不同的结果集,一行有三个聚合:
SELECT SUM(IF(b.id_status IN ( 1 ))) AS `AVAILABLE`
, SUM(IF(b.id_status IN ( 2,3,5 ))) AS `WAITING`
, SUM(IF(b.id_status NOT IN ( 1,2,3,5 ))) AS `DAMAGED`
FROM book_records b
JOIN book_class c
ON c.id_book = p.id_book
AND c.class_desc = 'NOVEL'
WHERE b.id_status IS NOT NULL
要将结果转换为当前查询返回的三行,我们可以生成您想要返回的三行(内联视图别名为s),然后匹配上面查询的计数(如内联视图别名为t) 。我们将做一个CROSS JOIN(从t只有一行),并使用CASE表达式来挑选哪一行返回哪一行......
SELECT s.status
, CASE WHEN s.status = 'AVAILABLE' THEN t.count_available
WHEN s.status = 'WAITING' THEN t.count_waiting
WHEN s.status = 'DAMAGED' THEN t.count_damaged
END AS BIL
FROM ( SELECT 'AVAILABLE' AS status, 1 AS seq
UNION ALL
SELECT 'WAITING', 2
UNION ALL
SELECT 'DAMAGED', 3
) s
CROSS
JOIN ( SELECT IFNULL(SUM(IF(b.id_status IN ( 1 ))),0) AS count_available
, IFNULL(SUM(IF(b.id_status IN ( 2,3,5 ))),0) AS count_waiting
, IFNULL(SUM(IF(b.id_status NOT IN ( 1,2,3,5 ))),0) AS count_damaged
FROM book_records b
JOIN book_class c
ON c.id_book = p.id_book
AND c.class_desc = 'NOVEL'
WHERE b.id_status IS NOT NULL
) t
ORDER BY s.seq
(我看不到你的查询是如何运行的,因为它引用了k.id_book
但是没有k
行源。我会假设这应该是c.id_book;我没有看到b和c之间有任何连接条件,我不认为你的意思是做一个CROSS JOIN。我将假设b和c之间有一对多的关系,book_record可以有零,一个或多个book_class,但你只对特定类的book_record感兴趣。看起来你的每个查询都意味着执行GROUP BY来获取每个状态的计数......)
此外,最佳做法是限定引用多个行源的查询中的所有列引用。
要获得你要返回的结果集,我会做这样的事情,只需一次通过表格:
SELECT CASE WHEN b.id_status IN ( 1 ) THEN 'AVAILABLE'
WHEN b.id_status IN ( 2,3,5 ) THEN 'WAITING'
ELSE 'DAMAGED' // any non-NULL status that is not 1,2,3,5
END AS status
, COUNT(b.id_status) AS BIL
FROM book_records b
JOIN book_class c
ON c.id_book = p.id_book
AND c.class_desc = 'NOVEL'
WHERE b.id_status IS NOT NULL
GROUP BY status
如果保证id_status为NOT NULL,则可以省略该WHERE子句。
(不言而喻,book_class上的适当索引,包含id_book
的前导列,包括book_class
,或可能on (book_class, id_book)
,都会加快联接操作。我我假设id_book已经是book_record
的唯一索引。理想情况下,也会覆盖索引ON book_records (id_book,id_status)
。)
要获得相同的结果集,您返回的工作要多一些(保证三行,零计数),但它仍然比运行三个查询更有效:
SELECT s.status AS STATUS
, IFNULL(t.BIL,0) AS BIL
FROM ( SELECT 'AVAILABLE' AS status, 1 AS seq
UNION ALL
SELECT 'WAITING', 2
UNION ALL
SELECT 'DAMAGED', 3
) s
LEFT
JOIN ( SELECT CASE WHEN b.id_status IN ( 1 ) THEN 'AVAILABLE'
WHEN b.id_status IN ( 2,3,5 ) THEN 'WAITING'
ELSE 'DAMAGED' // any non-NULL status that is not 1,2,3,5
END AS status
, COUNT(b.id_status) AS BIL
FROM book_records b
JOIN book_class c
ON c.id_book = p.id_book
AND c.class_desc = 'NOVEL'
WHERE b.id_status IS NOT NULL
GROUP BY status
) t
ON t.status = s.status
ORDER BY s.seq
答案 1 :(得分:0)
使用union all
。 union
删除重复项,这是不必要的开销:
SELECT 'AVAILABLE' AS STATUS, count(id_status) as BIL
FROM
book_records AS b, book_class AS c
WHERE p.id_book = k.id_book AND id_status IN ( 1 ) AND class_desc = 'NOVEL'
UNION ALL
SELECT 'WAITING' AS STATUS, count(id_status) as BIL
FROM
book_records AS b, book_class AS c
WHERE p.id_book = k.id_book AND id_status IN ( 2,3,5 ) AND class_desc = 'NOVEL'
UNION ALL
SELECT 'DAMAGED' AS STATUS, count(id_status) as BIL
FROM
book_records AS b, book_class AS c
WHERE p.id_book = k.id_book AND id_status NOT IN ( 1,2,3,5 ) AND class_desc = 'NOVEL';
注意:我故意在查询中留下了查询错误(例如不匹配的别名)。这是发布的原始查询。我假设这是将查询标识符从另一种语言翻译成英语的工件。
编辑:
此查询根本不需要union
。代替:
select (case when id_status in ( 1 ) then 'Available'
when id_status in (2, 3, 5) then 'Waiting'
else 'Damaged'
end),
count(*) as cnt
from book_records br join
book_class bc
on br.id_book = pc.id_book
where bc.class_desc = 'NOVEL'
group by (case when id_status in ( 1 ) then 'Available'
when id_status in (2, 3, 5) then 'Waiting'
else 'Damaged'
end);
此查询将使用book_class(class_desc, id_book)
和book_records(id_book)
上的索引运行得更快。
答案 2 :(得分:0)
首先,你不需要UNION
来做你正在做的事情。您可以使用SELECT
在一个CASE
中完成所有操作。您还需要正确分组聚合函数:
SELECT
CASE
WHEN id_status = 1 THEN 'AVAILABLE'
WHEN id_status IN (2, 3, 5) THEN 'WAITING'
ELSE 'DAMAGED'
END AS status, count(id_status) as BIL
FROM book_records b
LEFT JOIN book_class c ON b.id_book = c.id_book
WHERE c.class_desc = 'NOVEL'
GROUP BY status;
答案 3 :(得分:0)
CREATE INDEX br_idx ON book_records (id_book, id_status);
CREATE INDEX bc_idx ON book_class (id_book, class_desc);