暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

新的SQL格式化输出工具——基于解析,而不是regexp

原创 eternity 2022-09-05
817

很长一段时间,我一直在寻找SQL漂亮的打印机。

这些年来,我不得不处理的一些问题,比如:

SELECT
DECODE(a.category,NULL,b.category,a.category) "category",b.par "Total object Request",b.ps "objects Served",
b.ar "Total sushi Request", a.sushis "sushis Served",
ROUND((DECODE(b.ar,0,0,(b.ar - CAST(DECODE(a.sushis,NULL,0,a.sushis) AS numeric))/b.ar))*100,3) "USR",
a.clk "points", DECODE(b.ps,0,0,ROUND((a.clk/b.ps)*100,3)) "CTR",a.cpc "CPC", a.tc "Cost",
DECODE(b.ps,0,0,CAST((a.tc/b.ps)*1000 AS numeric(8,3))) "effectcost"
FROM
(SELECT
DECODE(b.category,NULL,'N/A',b.category) category, SUM(doughnuts) sushis, SUM(points) clk,
ROUND(CAST(SUM(total_cost) AS numeric),3) tc,
DECODE(SUM(points),0,0,ROUND(CAST((SUM(total_cost)/SUM(points)) AS numeric),3)) cpc
FROM
daily_city_dealer_summary a, category_dealer_map b
WHERE
a.category_dealer_id=b.category_dealer_id  AND   created_day BETWEEN '2010-05-01' AND '2010-05-25'
GROUP BY b.category) a     full outer join
 
(SELECT
DECODE(a.category,NULL,'N/A', DECODE(a.category,'-','World-Remaining countries',a.category)) category,
SUM(valid_object_request) par, SUM(valid_sushi_request) ar, SUM(object_doughnuts) ps
FROM
traffic_hit a
WHERE
request_date BETWEEN '2010-05-01' AND '2010-05-25'
GROUP BY a.category)  b
ON LOWER(a.category)=LOWER(b.category)
ORDER BY 4 DESC;

多年来,格式化查询的最佳方式(在我看来)是来自pgFormatter项目的pg_format程序

这确实令人印象深刻,但有一个重要的缺点——重新格式化是通过应用一些相当复杂的regexp和定制的标记器来完成的。

我梦见了一些能够实际解析查询的东西,并基于解析树使用一些一致的逻辑重建查询,有libpg_query库,基于它的是Ruby GEM。这段代码使用从源代码中提取的Pg解析器。并将任意查询解析为相对容易使用的树。

基于此,我编写了解析器微服务,然后用perl编写了漂亮的打印机。

今天我改变了explain.com和paste.depesz.com使用此新代码进行漂亮打印。

如果一些数据无法解析,或者我的代码不支持它(目前),两个站点都会使用pgFormatter。

此外,我还添加了一种使用paste.depesz进行漂亮打印的方法,甚至不在paste.depesz上保存数据.com网站,使用/prettify url。

您可以这样使用它:

=$ echo 'select a from b' | curl -s -XPOST --data-urlencode "q@-" https://paste.depesz.com/prettify
SELECT
    a
FROM
    b;
-- Formatted by Pg::SQL::PrettyPrinter

您可以自己编写一个简单的脚本,如/usr/local/bin/prettify。sh内容:

#!/usr/bin/env bash
 
curl -s -XPOST --data-urlencode "q@${1:--}" https://paste.depesz.com/prettify

然后您也可以将其用于文件:

=$ prettify.sh z.sql 
SELECT
    123;
-- Formatted by Pg::SQL::PrettyPrinter

所有经过修饰的SQL(通过/prettify,或在explain.depesz.com上,或在

paste.depesz.com将在最后一行包含注释,说明使用了哪个美化库。

因此,如果您的查询没有被Pg::SQL::PrettyPrinter修饰,那么您认为应该这样做。或者是,但方式不正确,请通过填写问题与我联系。

目前只处理DML查询(选择、插入、更新、删除),但我将在将来添加DDL

希望你会发现它有用。

此外,我还要感谢亚历山大·费利佩(bob)和安德烈亚斯·舍尔巴姆(Andreas‘ads’Scherbaum)督促我完成这项工作。如果没有你,我可能不会这么做

最后,作为一个快速示例,在使用Pg SQL PrettyPrinter格式化后,从post开始的查询如下所示:

SELECT
    decode(
        a.category,
        NULL,
        b.category,
        a.category
    ) AS category,
    b.par AS "Total object Request",
    b.ps AS "objects Served",
    b.ar AS "Total sushi Request",
    a.sushis AS "sushis Served",
    round(
        decode(
            b.ar,
            0,
            0,
            ( b.ar - decode( a.sushis, NULL, 0, a.sushis )::pg_catalog.numeric ) / b.ar
        ) * 100,
        3
    ) AS "USR",
    a.clk AS points,
    decode(
        b.ps,
        0,
        0,
        round(
            ( a.clk / b.ps ) * 100,
            3
        )
    ) AS "CTR",
    a.cpc AS "CPC",
    a.tc AS "Cost",
    decode(
        b.ps,
        0,
        0,
        ( a.tc / b.ps ) * 1000::pg_catalog.numeric( 8, 3 )
    ) AS effectcost
FROM
    (
        SELECT
            decode(
                b.category,
                NULL,
                'N/A',
                b.category
            ) AS category,
            sum( doughnuts ) AS sushis,
            sum( points ) AS clk,
            round(
                sum( total_cost )::pg_catalog.numeric,
                3
            ) AS tc,
            decode(
                sum( points ),
                0,
                0,
                round(
                    sum( total_cost ) / sum( points )::pg_catalog.numeric,
                    3
                )
            ) AS cpc
        FROM
            daily_city_dealer_summary AS a,
            category_dealer_map AS b
        WHERE
            a.category_dealer_id = b.category_dealer_id AND
            created_day BETWEEN '2010-05-01' AND '2010-05-25'
        GROUP BY
            b.category
    ) AS a
    FULL JOIN (
        SELECT
            decode(
                a.category,
                NULL,
                'N/A',
                decode(
                    a.category,
                    '-',
                    'World-Remaining countries',
                    a.category
                )
            ) AS category,
            sum( valid_object_request ) AS par,
            sum( valid_sushi_request ) AS ar,
            sum( object_doughnuts ) AS ps
        FROM
            traffic_hit AS a
        WHERE
            request_date BETWEEN '2010-05-01' AND '2010-05-25'
        GROUP BY
            a.category
    ) AS b ON lower( a.category ) = lower( b.category )
ORDER BY
    4 DESC;
-- Formatted by Pg::SQL::PrettyPrinter

也许并不理想,但这是一种改变。

斯蒂芬说:我认为格式化是人偏好的问题,我更喜欢使用新的格式化程序的case语句,因为它更多地在一行中。然而,我个人更喜欢在一行(或逻辑上属于一起的内容)上解码,而不是分散在这么多行中。但现在我必须搜索pg文档,我认为在这种情况下解码在Postgres中不存在,只有Oracle可以。

原文标题:New SQL pretty printer – based on parsing, and not regexps
原文链接:https://www.depesz.com/2022/08/31/new-sql-pretty-printer-based-on-parsing-and-not-regexps/

最后修改时间:2022-09-05 11:51:21
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论