如何简化此查询?
我想要做的是通过子查询派生列S9_Unlock,其中我只查找从主查询返回的user_ids,但这对我来说看起来很尴尬,特别是因为这里的查询只是一个摘录。实际上,我正在做这些子查询的多个来派生不同的列......
SELECT userid, CAST(to_char(S9_unlock,'YYYY/MM/DD') AS timestamp) AS "S9_Unlock"
FROM (
SELECT ca.user_id AS userid
FROM shop_db.invoices AS inv
LEFT JOIN shop_db.carts AS ca ON inv.id = ca.invoice_id
LEFT JOIN shop_db.cart_items AS ci ON ca.id = ci.cart_id
WHERE (inv.created BETWEEN '2014-11-13' AND '2014-11-14' OR inv.created BETWEEN '2013-11-14' AND '2013-11-15')
AND inv.status <> 'do_not_book'
AND inv.id IS NOT NULL
GROUP BY user_id) AS master
LEFT JOIN (
SELECT MIN(s3.unl) AS "S9_Unlock", s3.user_id
FROM (
SELECT user_id, challenge_codes.created AS unl,
MAX /* Check if license contains Suite9 */
(CASE WHEN substring(bundle_article_code,1,6) = 'BuSu90' THEN 1 ELSE 0 END) AS "S9_Unlock"
FROM licensing_db.serial_numbers
LEFT JOIN licensing_db.licenses ON licenses.id = serial_numbers.license_id
LEFT JOIN user_db.users ON users.id = licenses.user_id
LEFT JOIN licensing_db.challenge_codes ON challenge_codes.serial_number_id = serial_numbers.id
WHERE user_id IN (
SELECT ca.user_id AS userid
FROM shop_db.invoices AS inv
LEFT JOIN shop_db.carts AS ca ON inv.id = ca.invoice_id
LEFT JOIN shop_db.cart_items AS ci ON ca.id = ci.cart_id
WHERE (inv.created BETWEEN '2014-11-13' AND '2014-11-14' OR inv.created BETWEEN '2013-11-14' AND '2013-11-15')
AND inv.status <> 'do_not_book'
AND inv.id IS NOT NULL
GROUP BY user_id
)
GROUP BY user_id, challenge_codes.created) AS s3
)
WHERE "S9_Unlock" = 1
AND s3.unl IS NOT NULL
GROUP BY s3.user_id) AS "S9_Unlock" ON "S9_Unlock".user_id = master.userid
答案 0 :(得分:1)
在您的查询中,您有两个相同的子查询;这是一个CTE的尖叫声。
在有关许可问题的子查询中,您可以使用GROUP BY
子句在HAVING
子句后过滤掉有效许可证。也可以使它成为WITH QUERY
,最终会有更可读性:
WITH inv AS (
SELECT ca.user_id AS userid
FROM shop_db.invoices AS inv
LEFT JOIN shop_db.carts AS ca ON ca.invoice_id = inv.id
LEFT JOIN shop_db.cart_items AS ci ON ci.cart_id = ca.id
WHERE (inv.created BETWEEN '2014-11-13' AND '2014-11-14' OR inv.created BETWEEN '2013-11-14' AND '2013-11-15')
AND inv.status <> 'do_not_book'
AND inv.id IS NOT NULL
), s3 AS (
SELECT u.user_id, min(cc.created) AS first_unlocked, bundle_article_code
FROM licensing_db.serial_numbers AS sn
LEFT JOIN licensing_db.licenses AS lic ON lic.id = sn.license_id
LEFT JOIN user_db.users AS u ON u.id = lic.user_id
LEFT JOIN licensing_db.challenge_codes AS cc ON cc.serial_number_id = sn.id
WHERE u.user_id IN (SELECT userid FROM inv)
GROUP BY u.user_id, bundle_article_code
HAVING bundle_article_code LIKE 'BuSu90%'
AND first_unlocked IS NOT NULL
)
SELECT userid, date_trunc('day', first_unlocked) AS "S9_Unlock"
FROM inv
LEFT JOIN s3 ON s3.user_id = inv.userid;
因此,主查询现在减少到3行,并且WITH-QUERY
都执行数据库的逻辑自包含查询。您引用的其他子查询可以类似地成为WITH-QUERY
,然后在主查询中汇编它们。请记住,您可以在with-queries列表中引用先前命名的查询,如上所示inv
引用s3
。虽然这样的CTE在语法上不提供新功能(RECURSIVE
变体除外),但它们确实使复杂查询更具可读性,因此更易于维护。
另一种方法是将逻辑子组件(例如inv
子查询)分解出来并从中生成VIEW
。然后,您可以简单地引用主查询中的视图。如果您想使查询更灵活,那么将整个事物视为一个视图可能也是一个好主意。如果您想在2014年3月27日查询Suite9.1('BuSu91%')怎么办?取出这些文字,然后在视图中将它们用作WHERE
子句,使您的查询更加通用;这可以是子查询,也可以是完整的CTE。
(请检查s3
with-query中的语义是否仍然正确,因为如果没有您的表结构和示例数据,我无法测试上面的代码。)
答案 1 :(得分:0)
我不会将你的问题解决为一个巨大的单片关系sql查询,而是会认真考虑去&#34;程序&#34;通过使用内置的&#34; plpgsql&#34; postgresql的语言。这可以为您的应用程序带来很多清晰度。