SQL query to calculate a value from 3 different rows - sql

col1 col2 cal_val
F 1879 1879
% 25 1409
$ -45 1454
First row basically outputs back the entry in column 2
2nd row would basically calculate 25% of the value in row1, column 3 and then subtract that value from previous value which is 1879-470 = 1409
I need to be able to calculate final value of 1454 (which is basically a subtraction from the 2nd row output value)1409 - (-45) which would equal to 1454

You can use conditional aggregation:
select ( (sum(case when col1 = 'F' then col2 end) *
sum(case when col1 = '%' then 1 - col2 / 100.0 end)
) -
sum(case when col1 = '$' then col2 end)
)
from t;
Here is a db<>fiddle.

create temporary table t_teble
(
col1 text ,
col2 numeric ,
cal_val numeric );
insert into t_teble
select 'F',1879, 1879 union all
select '%',25,1409 union all
select '$',-45,1454;
select ( (sum(case when col1 = 'F' then col2 end) *
sum(case when col1 = '%' then 1 - col2 / 100.0 end)
) -
sum(case when col1 = '$' then col2 end)
),
(sum(case when col1 = 'F' then col2 end) *
sum(case when col1 = '%' then 1 - col2 / 100.0 end)
)
from t_teble;

Related

sql: count of all values in all columns and convert them into rows

I have a table that includes multiple columns (more than 200 cols) and all values in all cols are either 0 or 1, like the following:
col1
col2
col3
1
0
1
0
1
1
0
1
1
What I want to do is to count all of the zero and one values for each columns and display them row-wise like the following:
columns
one_values
zero_value
col1
1
2
col2
2
1
col2
3
0
I am trying to write my query like the following:
select 'col1',
sum(case when col1=1 then 1 else 0 end) one_values,
sum(case when col1=0 then 1 else 0 end) zero_values
from t
union all
select 'col2',
sum(case when col2=1 then 1 else 0 end) one_values,
sum(case when col2=0 then 1 else 0 end) zero_values
from t
however, since I have too many columns, I would get the resource exceed error. I was wondering if anyone can suggest a more efficient way to do this?
Thank you
Use below approach
select split(kv, ':')[offset(0)] as column,
countif(1 = safe_cast(split(kv, ':')[offset(1)] as int64)) as one_values,
countif(0 = safe_cast(split(kv, ':')[offset(1)] as int64)) as zero_values
from your_table t, unnest(split(translate(to_json_string(t), '{}"', ''))) kv
group by column
if applied to sample data in your question - output is

how to output result of group by of two columns with one column values as row and another as columns?

I have table like this
id col1 col2
1 A 1
2 B 0
3 A 1
4 C 1
5 B 0
6 A 0
7 C 1
8 C 1
9 B 1
10 B 0
I need a query something like this
Values 1 0
A 2 1
B 1 3
C 3 0
In the above result the header shows the col2 distinct values (1,0) and rows names represents distinct values of col1. The values in the table shows the counts.
Any suggestion to get the result like this in postgresql?
You need conditional aggregation :
select col1,
sum(case when col2 = 1 then 1 else 0 end) as 1,
sum(case when col2 = 0 then 1 else 0 end) as 0
from table t
group by col1;
You could also use FILTER:
SELECT
col1,
COUNT(*) FILTER (WHERE col2 = 1) AS 1,
COUNT(*) FILTER (WHERE col2 = 0) AS 0,
FROM
foo
GROUP BY
col1;
Here are simpler ways to write this logic. The first is Postgres-specific:
select col1,
sum( (col2 = 1)::int ) as num_1,
sum( (col2 = 0)::int as num_0
from t
group by col1;
The second just uses arithmetic:
select col1,
sum( col2 ) as num_1,
sum( 1 - col2 ) as num_0
from t
group by col1;

Return multiple rows from conditional group by without union

I am trying to build a query which supports conditional group by in SQLite DB.
Here is what I tried so far:
SELECT
case
when A>1 AND B>1 THEN 1
when X>1 AND Y>1 THEN 2
when C>1 AND D>1 THEN 3
END AS data_grp,
SUM(col1) AS col1,
SUM(col2) AS col2
FROM tbl
GROUP BY data_grp;
This Works pretty fine if only single case is true at a time. if multiple cases are true in a row then it returns the first case instead of all satisfying groups.
I have tried this by the union which works well but very slow. Is there any other way to fetch results fast with this conditional group.
Sample Data & Expected results:
DROP TABLE IF EXISTS tbl;
CREATE TABLE tbl
(
A INT,
B INT,
C INT,
D INT,
X INT,
Y INT,
col1 int,
col2 int
);
INSERT INTO tbl(A,B,C,D,X,Y,col1,col2) values (2,3,0,0,0,0,5,10);
INSERT INTO tbl(A,B,C,D,X,Y,col1,col2) values (0,0,0,0,8,10,3,2);
INSERT INTO tbl(A,B,C,D,X,Y,col1,col2) values (5,4,4,9,0,0,3,2);
SELECT
case
when A>1 AND B>1 THEN 1
when X>1 AND Y>1 THEN 2
when C>1 AND D>1 THEN 3
END AS data_grp,
SUM(col1) AS col1,
SUM(col2) AS col2
FROM tbl
GROUP BY data_grp;
Query Output :
"1" "8" "12"
"2" "3" "2"
Expected Output :
"1" "8" "12"
"2" "3" "2"
"3" "3" "2"
You can not use GROUP BY directly because of the overlapping groups.
You can use something like following, although this may also be slow.
WITH RECURSIVE
cnt(x) AS (
SELECT 1
UNION ALL
SELECT x+1 FROM cnt
LIMIT 3
)
SELECT x as data_grp, sum(col1), sum(col2)
FROM cnt,
(SELECT
case when A>1 AND B>1 THEN 1 ELSE 0 END as dg1,
case when X>1 AND Y>1 THEN 2 ELSE 0 END as dg2,
case when C>1 AND D>1 THEN 3 ELSE 0 END as dg3,
col1, col2
FROM tbl) t WHERE x=dg1 or x=dg2 or x=dg3
GROUP BY x
I am wary of summarizing data, where the result is on multiple rows and the totals don't match the original data. Of course, sometimes it is necessary, but here are two alternatives.
If you can be slightly flexible in your results, then you can concat the conditions together to get a more complex group:
SELECT ( (CASE WHEN A > 1 AND B > 1 THEN '1' ELSE '' END) ||
(CASE WHEN X > 1 AND Y > 1 THEN '2' ELSE '' END) ||
(CASE WHEN C > 1 AND D > 1 THEN '3' ELSE '' END)
) AS data_grp,
SUM(col1) AS col1, SUM(col2) AS col2
FROM tbl
GROUP BY data_grp;
I would actually write this as:
SELECT ( (CASE WHEN A > 1 AND B > 1 THEN '1' ELSE '0' END) ||
(CASE WHEN X > 1 AND Y > 1 THEN '1' ELSE '0' END) ||
(CASE WHEN C > 1 AND D > 1 THEN '1' ELSE '0' END)
) AS data_grp,
So data_grp gets a string of 0's and 1's indicating the group.
These results are not the same as your results. They are more what I would want, if I were looking at different groups -- I would want to see the overlaps between the groups.
Or, I would put the values in separate columns:
SELECT SUM(CASE WHEN A > 1 AND B > 1 THEN col1 ELSE 0 END) as sum1_1,
SUM(CASE WHEN X > 1 AND Y > 1 THEN col1 ELSE 0 END) as sum1_2,
SUM(CASE WHEN C > 1 AND D > 1 THEN col1 ELSE 0 END) as sum1_3,
SUM(CASE WHEN A > 1 AND B > 1 THEN col2 ELSE 0 END) as sum2_1,
SUM(CASE WHEN X > 1 AND Y > 1 THEN col2 ELSE 0 END) as sum2_2,
SUM(CASE WHEN C > 1 AND D > 1 THEN col2 ELSE 0 END) as sum2_3
FROM tbl;
These are the same results, but pivoted differently.

How to deal with duplicate rows in SQL?

The table has duplicate IDs from a large table. I want to get one output for each ID. What's the best way to do it?
MyTable
ID Col1 Col2
1 X A
1 Y B
1 Z C
2 X D
2 Y E
3 Z F
3 W G
If Col1 = 'X' and Col2 ='A', then 'Y' is the output for ID1
If Col1 = 'X' and Col2 !='A', then 'N' is the output for ID2
If Col1 != 'X', then 'Y' is the output for ID3
If Col1 = 'X' and Col2 ='A', then 'Y' is the output for ID1
If Col1 = 'X' and Col2 !='A', then 'N' is the output for ID2
If Col1 != 'X', then 'Y' is the output for ID3
I assume the conditions above need to be true for only 1 row per id. You can use conditional aggregation to check whether the condition applies to at least 1 row per group:
select id,
case when count(case when Col1 = 'X' and Col2 = 'A' then 1 end) > 0 then 'Y'
when count(case when Col1 = 'X' and Col2 <> 'A' then 1 end) > 0 then 'N'
when count(case when Col1 = 'X' then 1 end) > 0 then 'Y'
else '?'
end as output
from mytable
group by id

Case when to find the difference in the number of counts of particular kind of rows.

I have a table which looks like this
col1, col2
a,C
a,D
a,C
a,D
I want to find out that for a in col1, what is the (Number of rows with C - Number of rows with D).
If I were to find the two numbers it will simply be
SELECT COUNT(1) FROM mytable where COL1='a' and COL2='C'
SELECT COUNT(1) FROM mytable where COL1='a' and COL2='D'
And then I could just find the difference.
However, I wanted to do it with a single query. So I went for this
SELECT COUNT(CASEWHEN(COL2)='D', 1, -1)
FROM mytable
But that does not seem to work. Any suggestions?
Here is one way:
SELECT SUM(CASE WHEN col2 = 'C' THEN 1 ELSE 0 END) Col2C,
SUM(CASE WHEN col2 = 'D' THEN 1 ELSE 0 END) Col2D,
SUM(CASE WHEN col2 = 'C' THEN 1 WHEN col2 = 'D' THEN -1 ELSE 0 END) [Col2C-Col2D]
FROM myTable
WHERE col1 = 'a'
select SUM(CASE WHEN col2 = 'C' THEN 1 WHEN col2 = 'D' THEN -1 ELSE 0 END) as Diff
from mytable
where col1 = 'a'
SELECT sum(CASE WHEN COL2='D' THEN 1 ELSE -1 END) as balance
FROM mytable
You were on the right track, this should work:
SELECT col1, SUM(CASE WHEN COL2 ='C' THEN 1 ELSE 0 END) - SUM(CASE WHEN COL2 ='D' THEN 1 ELSE 0 END)
FROM mytable
GROUP BY col1
Thanks folks for the quick responses. For me, the following code snippet worked.
SUM( CASEWHEN (COL2='D', 1, -1))
And a slightly fuller version of this is
SUM( CASE WHEN COL2='C' THEN -1 WHEN COL2='D' THEN 1 ELSE 0 END)
I would recommend the fuller version.