Do all groups have equal total power for given subgroup?

本小妞迷上赌 提交于 2019-12-13 19:00:20

问题


I have a PostgreSQL table like this:

CREATE TABLE foo (man_id, subgroup, power, grp)
AS VALUES
    ( 1, 'Sub_A',  1, 'Group_A' ),
    ( 2, 'Sub_B', -1, 'Group_A' ),
    ( 3, 'Sub_A', -1, 'Group_B' ),
    ( 4, 'Sub_B',  1, 'Group_B' ),
    ( 5, 'Sub_A', -1, 'Group_A' ),
    ( 6, 'Sub_B',  1, 'Group_A' ),
    ( 7, 'Sub_A', -1, 'Group_B' ),
    ( 8, 'Sub_B',  1, 'Group_B' );

The power calculation works like this:

Total Power of Subgroup Sub_A in the grp Group_A is (1 + (-1) ) = 0
Total Power of Subgroup Sub_B in the grp Group_A is ((-1) + 1 ) = 0
Total Power of Subgroup Sub_A in the grp Group_B is ((-1) + (-1) ) = -2
Total Power of Subgroup Sub_B in the grp Group_B is (1 + 1 ) = 2

So the power of Sub_A in the Group_A is not equal to power of Sub_A in the Group_B
So the power of Sub_B in the Group_A is not equal to power of Sub_B in the Group_B

I want to query the database with a subgroup name. If for a same subgroup name power is equal across all the other grp names, then it will return True, else False.

As an example, sub_A and sub_B both will return False. What would be the recommended way to do this?

I want something like:

SELECT * FROM foo (solution query will be added)
WHERE subgroup = 'sub_A'

And it returns False.


回答1:


Read the question carefully

I want to query the database with a subgroup name.

And:

I want something like

SELECT * FROM foo (solution query will be added)
WHERE subgroup = 'Sub_A'

The important point for performance is to exclude irrelevant rows early and only compute aggregates for the given subgroup. Then (assuming more than a few distinct subgroups), an index on (subgroup) can help:

CREATE INDEX ON foo (subgroup);

Each of the following queries returns FALSE if at least two groups have different total sums for the given subgroup, and TRUE in all other cases (with a minor exception for query 5, see below).

Query 1

SELECT count(DISTINCT total_power) = 1
FROM  (
   SELECT sum(power) AS total_power
   FROM   foo
   WHERE  subgroup = 'Sub_B'  -- exclude irrelevant rows early!
   GROUP  BY grp
   ) sub;

Query 2

SELECT count(*) = 1
FROM  (
   SELECT true
   FROM  (
      SELECT sum(power) AS total_power
      FROM   foo
      WHERE  subgroup = 'Sub_C'
      GROUP  BY grp
      ) sub2
   GROUP  BY total_power
   ) sub2;

Query 3

SELECT count(*) OVER () = 1
FROM  (
   SELECT sum(power) AS total_power
   FROM   foo
   WHERE  subgroup = 'Sub_A'
   GROUP  BY grp
   ) sub
GROUP  BY total_power
LIMIT  1;

Query 4

(
SELECT FALSE
FROM  (
   SELECT sum(power) AS total_power
   FROM   foo
   WHERE  subgroup = 'Sub_A'
   GROUP  BY grp
   ) sub
GROUP  BY total_power
OFFSET 1
LIMIT  1
)
UNION ALL
SELECT TRUE
LIMIT 1;

This one is special. Related answers with explanation:

  • Return a value if no record is found
  • Way to try multiple SELECTs till a result is available?

Query 5

SELECT min(total_power) = max(total_power)  -- can fail for NULL values
FROM  (
   SELECT sum(power) AS total_power
   FROM   foo
   WHERE  subgroup = 'Sub_A'
   GROUP  BY grp
   ) sub;

The last can fail if NULL values in power are allowed. (But you would have to define expected results in this case anyway.)

I ran an extensive test and found all queries to perform about the same under ideal conditions:

db<>fiddle here

Query 5 tended to be a tad bit faster than the rest.




回答2:


Assuming that in your CREATE TABLE statement, 'sub_A' is meant to be 'Sub_A' (because Postgres is case-sensitive), and that your power values are actually integers (if they aren't, just add casts; the code below is simpler without them), then you can calculate the power for each subgroup within a group as follows:

select
    subgroup,
    grp,
    sum(power) as sum_power
from
    foo
group by
    subgroup,
    grp

To determine whether all of the total power values for a subgroup are the same, just check that the minimum and maximum values are the same. Convert the previous query into a subquery, where the main query does that comparison, as follows:

select
    subgroup
from (
    select
        subgroup,
        grp,
        sum(power) as sum_power
    from
        foo
    group by
        subgroup,
        grp
    ) as subpwr
group by
    subgroup
having
    min(sum_power) = max(sum_power);



回答3:


Here's 1 way not yet mentioned by the other answers

SELECT SUM(power) = FIRST_VALUE(SUM(power)) OVER () powpow 
FROM foo
WHERE subgroup = 'Sub_A'
GROUP BY grp
ORDER BY powpow
LIMIT 1

-- returns:
-- false if some values differ
-- true if all values are the same
-- no rows if the where condition fails to match any rows.


来源:https://stackoverflow.com/questions/50918436/do-all-groups-have-equal-total-power-for-given-subgroup

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!