postgres類似oracle的decode實現


假設存在下面二張表

t_media:

mediaid   flags

11111    1<<17

22222    1<<18

33333    1<<17

44444    1<<2

t_media_locations:

mediaid  locationid

11111  999999

22222  999999  

33333    999999

現在有一個需求需要統計locationid=999999下面flags=1<<17和flag=1<<18的media數量,最初的丑陋實現如下:

SELECT
    a_tmp.locationid,a_tmp.attraction_count,r_tmp.restructant_count
FROM
    (
        SELECT
            COUNT(1)AS attraction_count,
            tmp.locationid
        FROM
            (
                SELECT
                    A . ID,
                    b.locationid
                FROM
                    t_media A,
                    t_media_locations b
                WHERE
                    A . ID = b.mediaid
                AND b.locationid IN(999999)
                AND A .flags & 134217728 > 0
                GROUP BY
                    A . ID,
                    b.locationid
            )tmp
        GROUP BY
            tmp.locationid
    )AS a_tmp,
    (
        SELECT
            COUNT(1)AS restructant_count,
            tmp.locationid
        FROM
            (
                SELECT
                    A . ID,
                    b.locationid
                FROM
                    t_media A,
                    t_media_locations b
                WHERE
                    A . ID = b.mediaid
                AND b.locationid IN(999999)
                AND A .flags & 268435456 > 0
                GROUP BY
                    A . ID,
                    b.locationid
            )tmp
        GROUP BY
            tmp.locationid
    )AS r_tmp
WHERE
    a_tmp.locationid = r_tmp.locationid


如此多的聯表,性能肯定很差,改進如下:

SELECT
sum ( CASE WHEN (flags & 268435456 > 0)  THEN 1 ELSE 0 END ),sum ( CASE WHEN (flags & 134217728 > 0)  THEN 1 ELSE 0 END ),b.locationid
FROM
    t_media a,
    t_media_locations b
WHERE
    a.id= b.mediaid
AND b.locationid IN(99999)
and a.flags is not null
group by b.locationid


小結:

1.如果不需要locationid,group by的操作也可以省略,注意,聚合函數本身針對多記錄操作並不一定是group by之后才使用,group by分組記錄也是多記錄。所以聚合函數使用的字段不需要出現在group by里。但是如果是直接select字段,postgres就需要group by該字段,有的db是不需要的比如sql lite.

2.postgres對於oracle中的decode函數的替代:CASE WHEN .....

3.flag這樣的type類型值在db中直接存儲為int,postgres的位與運算符&可以直接計算。

4.count(expr)如果expr是一個boolean值,count會被誤用。

錯誤的使用方式:

SELECT
--錯誤的統計行數的方式
count((flags & 134217728 > 0) )
FROM
    t_media a,
    t_media_locations b
WHERE
    a.id= b.mediaid
AND b.locationid IN(999999)

正確的方式就是case when使用之后sum:

SELECT
sum( CASE WHEN (flags & 134217728 > 0)  THEN 1 ELSE 0 END )
FROM
    t_media a,
    t_media_locations b
WHERE
    a.id= b.mediaid
AND b.locationid IN(999999)

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM