I need a query that can calculate a value based on conditions - sql

I have data such as:
Type
Amount
a
1000
a
5000
b
4000
b
2000
c
300
And would like to sum the amounts where Type is a and b, and minus the amounts where type is c.
I only know how to sum based on one condition, ie:
select sum(amount)
from xxxx
where type = 'a'
Do I need to do a sub-select or is there an easier way?

You can use a case statement inside sum:
select sum(case when type in ('a', 'b') then amount when type = 'c' then -amount end)
from table_name;

Use GROUP BY:
SELECT
Type,
sum(Amount)
FROM table
GROUP BY Type
https://www.postgresql.org/docs/10/queries-table-expressions.html#QUERIES-GROUP
https://www.postgresql.org/docs/10/tutorial-agg.html

WITH CTE(Type , Amount) AS
(
SELECT 'a' ,1000 UNION ALL
SELECT 'a' , 5000 UNION ALL
SELECT 'b' , 4000 UNION ALL
SELECT 'b' , 2000 UNION ALL
SELECT 'c' , 300
)
SELECT
SUM(CASE WHEN C.TYPE IN ('a','b')THEN C.Amount
ELSE 0
END) -
SUM(CASE WHEN C.TYPE='c' THEN C.Amount
ELSE 0
END)
FROM CTE AS C

for mariaDB
SELECT
debit - credit
FROM
(SELECT sum(Amount) AS debit FROM Your_table WHERE Type IN ('a', 'b')) AS condition1,
(SELECT sum(Amount) AS credit FROM Your_table WHERE Type NOT IN ( 'a', 'b' )) AS condition2;

Related

I want to get difference of sum of a column from two tables

I have 2 tables: transactions and transactions_archive. Each of them has fields accountno,drcr(which has values either as C or D) and field amount. I want to get difference of sum of all 'C' in both transactions and transactions_archive and sum of all 'D' in both transactions and transactions_archive.
What query can I use to get this answer.
I tried this unsuccessfully:
select (
select accountno,drcr,sum(amount)as total from
(
select accountno,drcr,amount
from ebank.tbtransactions
where drcr='C'
union all
select accountno,drcr,amount
from ebank.tbtransactions_archive
where drcr='C'
)
)
-
(select accountno,drcr,sum(amount)as total
from (
select accountno,drcr,amount
from ebank.tbtransactions
where drcr='D'
union all
select accountno,drcr,amount
from ebank.tbtransactions_archive
where drcr='D'
)
)
group by accountno,drcr;
If I understand correctly, you want to subtract all the "D"s from the "C"s. Combine the tables using UNION ALL and use conditional aggregation:
select accountno,
sum(case when drcr = 'C' then amount else - amount end)as total
from ((select accountno, drcr, amount
from ebank.tbtransactions
) union all
(select accountno, drcr, amount
from ebank.tbtransactions_archive
)
) t
where drcr in ('D', 'C')
group by accountno;
SELECT top 1 (amount - nextamount) as diff from(
SELECT
amount,LEAD(amount, 1,0) OVER (ORDER BY YEAR(drcr)) AS nextamount FROM(
SELECT drcr, sum(amount) as amount from transactions JOIN transactions_archive on transactions.drcr and transactions_archive.drcr GROUP BY drcr))

How to find the common values for a column which is present in many tables

I have a lot of tables in oracle DB. All tables have one common column named as 'COLUMN_FILTER' .All tables have lots of unique values for 'COLUMN_FILTER'. Is there any way to find the common records for 'COLUMN_FILTER' , present in all tables ? For sample please refer to the below scenario, where I have provided unique values for 'COLUMN_FILTER'.
Table A: 'X','Y','Z'
Table B: 'W','X'
Table C: 'Z'
Table D: 'Y','Z'
I am expecting the output to be 'Z','W' (any possible minimum set). So that I can put this filter on all tables.
Use HAVING COUNT(*)>1 clause along with GROUPing BY that column
SELECT column_filter
FROM
(
SELECT column_filter FROM tableA
UNION ALL
SELECT column_filter FROM tableB
UNION ALL
SELECT column_filter FROM tableC
UNION ALL
SELECT column_filter FROM tableD
)
GROUP BY column_filter
HAVING COUNT(*)>1
To find out the most common element you could have a query as follows
The highest value of cnt_vals indicate the tables which has the most common values of common_filter across all tables
select table_name
,column_filter
,count(column_filter) over(partition by 1) as cnt_vals
from (
select distinct column_filter,'A' as table_name
from tablea
union all
select distinct column_filter,'B' as table_name
from tableb
union all
select distinct column_filter,'C' as table_name
from tablec
union all
select distinct column_filter,'D' as table_name
from tabled
)x
order by 3 desc
You can find pairs using a join:
with f as (
select distinct column_filter, 'A' as table_name
from tablea
union all
select distinct column_filter, 'B' as table_name
from tableb
union all
select distinct column_filter, 'C' as table_name
from tablec
union all
select distinct column_filter, 'D' as table_name
from tabled
)
select f1.column_filter, f2.column_filter
from f f1 join
f f2
on f1.column_filter < f2.column_filter
group by f1.column_filter, f2.column_filter
having sum(case when 'A' in (f1.table_name, f2.table_name) then 1 else 0 end) > 0 and
sum(case when 'B' in (f1.table_name, f2.table_name) then 1 else 0 end) > 0 and
sum(case when 'C' in (f1.table_name, f2.table_name) then 1 else 0 end) > 0 and
sum(case when 'D' in (f1.table_name, f2.table_name) then 1 else 0 end) > 0;
You can easily extend this for triples:
with f as (
select distinct column_filter, 'A' as table_name
from tablea
union all
select distinct column_filter, 'B' as table_name
from tableb
union all
select distinct column_filter, 'C' as table_name
from tablec
union all
select distinct column_filter, 'D' as table_name
from tabled
)
select f1.column_filter, f2.column_filter
from f f1 join
f f2
on f1.column_filter < f2.column_filter join
f f3
on f3.column_filter < f2.column_filter
group by f1.column_filter, f2.column_filter, fd.column_filter
having sum(case when 'A' in (f1.table_name, f2.table_name, f3.table_name) then 1 else 0 end) > 0 and
sum(case when 'B' in (f1.table_name, f2.table_name, f3.table_name) then 1 else 0 end) > 0 and
sum(case when 'C' in (f1.table_name, f2.table_name, f3.table_name) then 1 else 0 end) > 0 and
sum(case when 'D' in (f1.table_name, f2.table_name, f3.table_name) then 1 else 0 end) > 0;
It is possible to express this as a recursive query, if you don't want to try more and more combinations. In Oracle 12C+, I would recommend a recursive CTE. In earlier versions, you have to adapt using connect by.

Select Customer ID who hasnt purchased product X

I have a table of customer IDs and Products Purchased. A customer ID can purchase multiple products over time.
customerID, productID
In BigQuery I need to find the CustomerID for those who have not purchased product A.
I've been going around in circles trying to do self joins, inner joins, but I'm clueless.
Any help appreciated.
select customerID
from your_table
group by customerID
having sum(case when productID = 'A' then 1 else 0 end) = 0
and to check if it only contains a name
sum(case when productID contains 'XYZ' then 1 else 0 end) = 0
Below is for BigQuery Standard SQL
#standardSQL
SELECT CustomerID
FROM `project.dataset.yourTable`
GROUP BY CustomerID
HAVING COUNTIF(Product = 'A') = 0
You can test / play with it using dummy data as below
#standardSQL
WITH `project.dataset.yourTable` AS (
SELECT 1234 CustomerID, 'A' Product UNION ALL
SELECT 11234, 'A' UNION ALL
SELECT 4567, 'A' UNION ALL
SELECT 7896, 'C' UNION ALL
SELECT 5432, 'B'
)
SELECT CustomerID
FROM `project.dataset.yourTable`
GROUP BY CustomerID
HAVING COUNTIF(Product = 'A') = 0
how would I adjust this so it could be productID contains "xyz"
#standardSQL
WITH `project.dataset.yourTable` AS (
SELECT 1234 CustomerID, 'Axyz' Product UNION ALL
SELECT 11234, 'A' UNION ALL
SELECT 4567, 'A' UNION ALL
SELECT 7896, 'Cxyz' UNION ALL
SELECT 5432, 'B'
)
SELECT CustomerID
FROM `project.dataset.yourTable`
GROUP BY CustomerID
HAVING COUNTIF(REGEXP_CONTAINS(Product, 'xyz')) = 0
If you have a customer table, you might want:
select c.*
from customers c
where not exists (select 1 from t where t.customer_id = c.customer_id and t.proectID = 'A');
This will return customers who have made no purchases as well as those who have purchased all but product A. Of course, the definition of a customer in your data might be that the customer has made a purchase, in which case I like Juergen's solution.

How to use decode or case function to build a condition base on the result of two records?

Let's say if I search by a key, it returns 2 records with 2 different values for each record.
It will return value 'A' and value 'B' for the 1st and 2nd records respectively.
ID VALUE
1 A
1 B
If the returned records contains 'A' and 'B' then I want to change all their value to 'C'.
If the returned record only contains 'A' or 'B' then i don't want to change to 'C'
How do i use the decode or case function to do that?
I tried (Case when value in('A','B') then 'C' else value end)
but it also changes the records that only returns either 'A' or 'B' to 'C'
So basically if my result are like this :
ID VALUE
1 A
1 B
I want it to be like this
ID VALUE
1 C
1 C
If the result is
ID VALUE or ID VALUE
1 A 1 B
1 A 1 B
Then don't implement the above conversion rule.
Edit for clarity
select id, value from t1
where id =123
gives me below
ID VALUE
1 A
1 B
I want a condition that uses the value of the two records--change the value to 'C' only when clm1.value=A and clm2.value=B
something like below but it does not work.
select id,
case when value ='A' and value ='B' then 'C' else value end
from t1
where id=123
Sorry for the confusion.
Thanks
What about something like this:
create table csm (id int, value varchar(5))
insert into csm (id,value)
SELECT 1,'A' UNION
SELECT 1,'B' UNION
SELECT 2,'A' UNION
SELECT 3,'B' UNION
SELECT 4,'A' UNION
SELECT 4,'B' UNION
SELECT 4,'D'
SELECT t.id
, case when tsub.TotalTimes=2 AND tsub.NumTimes=2 THEN 'C' ELSE t.value END as Value
FROM csm t
JOIN (
SELECT id, COUNT(DISTINCT CASE WHEN value IN ('A','B') THEN value END) AS NumTimes
, COUNT(DISTINCT value) TotalTimes
FROM csm
GROUP BY id
) AS tsub ON t.id=tsub.id
I get the following output:
1 C
1 C
2 A
3 B
4 A
4 B
4 D
The subquery finds out the number of times A and B occur for that id, and then your case statement checks if that value is 2, and if so changes it to C.
Seems like a perfect match for an analytic function:
with v_data(id, value) as (
select 1, 'A' from dual union all
select 1, 'B' from dual union all
select 2, 'A' from dual union all
select 3, 'B' from dual union all
select 3, 'B' from dual
)
select
v1.*,
(case
when v1.cnt_distinct > 1 then 'C'
else v1.value end)
as new_value
from (
select
id,
value,
count(*) over (partition by id) as cnt_overal,
count(distinct value) over (partition by id) as cnt_distinct
from v_data)
v1
This computes the number of distinct values for each ID (using count(distinct...) and then replaces the values with C if the number of distinct values is larger than 1.

SQL Select for multiple where clause

I am trying to create SQL Select that returns counts of a certain field based on a field.
So, here is what I am trying to do.
Select count(distinct id) as TotalCount, -- this will be the total of id
count(distinct id where type='A') as TotalA, -- this will be total when type='A'
count(distinct id where type='B') as TotalB -- This will be total when type = 'B'
from MyTable
Basically, TotalCount = TotalA + TotalB.
How can I achieve this in SQL Select Statement?
Thanks.
Select count(distinct id) as TotalCount, -- this will be the total of id
count(distinct case type when 'A' then id else NULL end) as TotalA,
count(distinct case type when 'B' then id else NULL end) as TotalB
from MyTable;
Of course TotalCount may or may not be TotalA + TotalB, depending on the actual data.
You can do it like that:
SELECT
count(distinct id) as TotalCount,
sum(CASE WHEN type = 'A' THEN 1 ELSE 0) as TotalA,
sum(CASE WHEN type = 'B' THEN 1 ELSE 0) as TotalB,
FROM
MyTable
Count per type:
SELECT
type,
count(DISTINCT id)
FROM
MyTable
GROUP BY
type
Why not simply UNION the separate queries.
Select 'all' as which, count(distinct id) as Total from mytable
union
select 'a' as which, count(distinct id) where type='A' as Total from mytable
union
select 'b' as which, count(distinct id) where type='B' as Total from mytable