sql - 加入多对多关系

标签 sql postgresql

我有三个表:applications、permissions 和 applications_permissions

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

对于应用程序,有两类:免费的和商业的(price = '0' 和 price != '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')

我该怎么做? 我现在已经尝试了几个小时(那个查询,misc JOINs)但它让我望而却步:/

最佳答案

简化。初稿非常理想。
在一个查询中计算全部:

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'

这包括完全没有附加应用程序权限(LEFT JOIN)。

如果存在未附加到任何权限应用程序,则列表加起来不会达到 100%。

我做了一次总计数 (ct) 并将其转换为子查询中的 float。剩下的计算可以用整数运算来完成,只有最后的 /ct 将数字转换为 float 。这是最快和最精确的。


与 CTE 相同

如果您对更多新事物持开放态度:尝试使用 CTEs (Common Table Expressions - WITH queries) 进行相同操作- 自 PostgreSQL 8.4 起可用。
它更干净并且可能稍微快一些,因为我在一个 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;

关于sql - 加入多对多关系,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10485414/

相关文章:

sql - 在 SQL 查询的 WHERE 条件中使用 Oracle SQL 函数的输出

c# - 无法让我的更新工作

java - 使用 java 代码列出特定 PostgreSQL 数据库中的所有表

sql - 用于在数据库中查找用户的 SQL 查询的性能

mysql - 从搜索记录中获取搜索次数最多的术语,忽略任何用户在给定时间范围(小时)内对搜索术语的任何重复条目

关于一次打印3个表的SQL查询

postgresql - 如何创建触发器来更新另一个表中的列?

postgresql - 锁定 PostgreSQL 中的公共(public)组角色

ruby-on-rails - 尝试在本地访问我的应用程序时,我得到 : "could not translate host name ... to address" and "Temporary failure in name resolution"

sql - 在 SQLite UPDATE 中模拟 ORDER BY 来处理唯一性约束