3

applications、permissions、applications_permissions の 3 つのテーブルがあります。

|------------|   |------------------------|   |-----------|
|applications|   |applications_permissions|   |permissions|
|------------|   |------------------------|   |-----------|
| id         | <-| application_id         |   | id        |
| price      |   | permission_id          |-> | name      |
|------------|   |------------------------|   |-----------|

アプリケーションには、無料と商用の 2 つのカテゴリがあります (価格 = '0' および価格 != '0')。

ここで、すべてのアクセス許可について、アプリケーション全体の何パーセントがアクセス許可を参照しているかを知りたいと思います。そして、これは両方のカテゴリで

無料:

id, percentage
1 , 20.0230
2 ,  0.0000
3 ,  0.0312
...

商業:

id, percentage
1 , 18.0460
2 ,  0.0000
3 ,  0.0402
...

次のクエリを作成しましたが、アプリケーションのないアクセス許可 ID は含まれていません:/

SELECT (SELECT name FROM permissions WHERE id = applications_permissions.permission_id) AS "name",
        100::float * COUNT(*)/(SELECT COUNT(name) FROM applications WHERE price = \'0\') AS "percent"
  FROM applications, applications_permissions
  WHERE applications.id = applications_permissions.application_id 
    AND applications.price = \'0\'
  GROUP BY applications_permissions.permission_id
  ORDER BY percent DESC')

どうすればいいですか?私は今数時間試してきました(そのクエリ、その他のJOIN)が、それは私を逃れます:/

4

4 に答える 4

4

簡略化。最初のドラフトは超最適でした。
すべてを 1 つのクエリで計算するには:

SELECT p.id
     ,(100 * sum((a.price > 0)::int)) / cc.ct AS commercial
     ,(100 * sum((a.price = 0)::int)) / cf.ct AS free
FROM  (SELECT count(*)::float AS ct FROM applications WHERE price > 0) AS cc
     ,(SELECT count(*)::float AS ct FROM applications WHERE price = 0) AS cf
      ,permissions p
LEFT   JOIN applications_permissions ap ON ap.permission_id = p.id
LEFT   JOIN applications a ON a.id = ap.application_id
GROUP  BY 1, cc.ct, cf.ct
ORDER  BY 2 DESC, 3 DESC, 1;

あなたの価格が実際には数値列であると仮定すると0'0'.

これにはpermissions、まったく添付applicationsされていないものも含まれます ( LEFT JOIN)。

applicationsどのリストにも添付されていないものが存在する可能性がある場合permissions、リストの合計は 100% になりません。

合計カウント ( ) を 1 回実行し、サブクエリでctキャストします。float残りの計算は整数演算で行うことができ、最後の数値のみが/ ct浮動小数点数に変換されます。これは最も高速で正確です。


CTEと同じ

さらに新しいものを受け入れる場合: CTE (共通テーブル式 - WITH クエリ)で同じことを試してください- PostgreSQL 8.4 以降で利用可能です。
私は両方のカウントを 1 つの CTE で実行し、より安価であるため、よりクリーンでおそらくわずかに高速です。どちらもGROUP BYサブクエリでも同様に実行できます。

WITH  c AS (
    SELECT sum((a.price > 0)::int) AS cc
          ,sum((a.price = 0)::int) AS cf
    FROM   applications
    ), p AS (
    SELECT id
          ,sum((a.price > 0)::int) AS pc
          ,sum((a.price = 0)::int) AS pf
    FROM   permissions p
    LEFT   JOIN applications_permissions ap ON ap.permission_id = p.id
    LEFT   JOIN applications a ON a.id = ap.application_id
    GROUP  BY 1
    )
SELECT p.id
     ,(100 * pc) / cc::float AS commercial
     ,(100 * pf) / cf::float AS free
FROM   c, p
ORDER  BY 2 DESC, 3 DESC, 1;
于 2012-05-07T18:33:06.697 に答える
3

使用LEFT OUTER JOIN:

SELECT * FROM permissions LEFT OUTER JOIN
applications_permissions as rel on permissions.id = rel.permission_id LEFT OUTER JOIN
applications on rel.application_id = applications.id
于 2012-05-07T16:18:17.630 に答える
1

これは機能しますか?

場合free

SELECT p.id, (100::float * COUNT(p.id)/(SELECT COUNT(*) from Applications)) Percent
FROM Applications a, Permissions p, Applications_Permissions a_p
WHERE a.id = a_p.application_id AND p.id = a_p.permission_id AND a.price = 0
GROUP BY p.id
ORDER BY Percent DESC
于 2012-05-07T16:19:07.270 に答える
1

1 つのクエリの結果は次のとおりです。

SELECT p.id
, p.name
, (CASE WHEN total.free=0 THEN NULL ELSE 100::float * sub.free::float / total.free::float END) AS percent_free
, (CASE WHEN total.comm=0 THEN NULL ELSE 100::float * sub.comm::float / total.comm::float END) AS percent_comm
FROM permissions AS p
LEFT JOIN (
  SELECT permission_id
  , SUM(CASE WHEN a.price<=0 THEN 1 ELSE 0 END) AS free
  , SUM(CASE WHEN a.price>0  THEN 1 ELSE 0 END) AS comm
  FROM applications_permissions AS pa
  JOIN applications AS a ON (pa.application_id=a.id)
  GROUP BY permission_id
) AS sub ON (p.id=sub.permission_id)
, (
  SELECT
    SUM(CASE WHEN price<=0 THEN 1 ELSE 0 END) AS free
  , SUM(CASE WHEN price>0  THEN 1 ELSE 0 END) AS comm
  FROM applications
) AS total

または、無料のアプリケーションのみの結果 (where 句を変更することにより、それぞれ商用アプリケーション):

SELECT p.id
, p.name
, (CASE WHEN total.nbr=0 THEN NULL ELSE 100::float * sub.nbr::float / total.nbr::float END) AS percent
FROM permissions AS p
LEFT JOIN (
  SELECT permission_id, COUNT(*) AS nbr
  FROM applications_permissions AS pa
  JOIN applications AS a ON (pa.application_id=a.id)
  WHERE (a.price<=0)
  GROUP BY permission_id
) AS sub ON (p.id=sub.permission_id)
, (
  SELECT COUNT(*) AS nbr
  FROM applications
  WHERE (price<=0)
) AS total
于 2012-05-07T23:45:08.137 に答える