Calculating a plug/balance in SQL - sql

My current table has a few accounts which always have minimal values (example, X & Y for actual and AA, BB, CC & DD for plan) in current table. Accounts with minimal values can extend beyond these accounts. I need to categorize such accounts into proxy account called "balance" which would always be [final-(abc+def+ghi)](please refer to Expected table). I'm trying it difficult to construct this in SQL using
sum (case when account not in ('abc','def','ghi').
Sample dataset here: http://sqlfiddle.com/#!9/114cfe/1
SQL attempt
select year, month, type,
case when accounts in 'abc' then 'abc'
case when accounts in 'def' then 'def'
case when accounts in 'ghi' then 'ghi'
case when accounts in 'final' then 'final'
else 'balance'
end as account_2
,
(
(case when accounts in ('abc','def','ghi','final') then sum(amount)
else
(
(case when accounts in ('final') then sum(amount))-
sum(case when accounts in ('abc','def','ghi') then sum(amount) else 0)))
)

I think it would be easier to split it into two steps. Change the values in one step and group the rows in another.
SELECT year, month, type, account_2, SUM(amount) AS amount_2
FROM (
SELECT year, month, type, amount,
CASE WHEN accounts IN ('abc', 'def', 'ghi', 'final') THEN accounts
ELSE 'balance' END AS account_2
FROM someTable
) AS tmp
GROUP BY year, month, type, account_2

Related

Combine different results of "group by" queries in the same table

I need to make some comparation between 2 years: sales by product, sales by category, etc.
How can I have this in one table having 3 columns:
first column = product, category, etc
second column = sales in 2021
third column = sales in 2022
Sample of queries that must be combined in one single table as the one below
select product_code, sum(amount)
from product
where year = '2021'
group by product_code
select product_code, sum(amount)
from product
where year = '2022'
group by product_code
select category_code, sum(amount)
from category
where year = '2021'
group by category_code
select category_code, sum(amount)
from category
where year = '2022'
group by category_code
Please, see the final table
[1]: https://i.stack.imgur.com/smF7h.png
NOTE!
If for instance in 2021 there was no "product D", it will be 0 for "Sales_2021" or the "product A" is no longer present in 2022, it will be 0 for "Sales_2022".
Thank you
You need two things here:
Conditional aggregation (a CASE expression inside the aggregation function) in order to get 2021 and 2022 in one go.
A union of two intermediate result sets (product figures UNION ALL category figures).
And as any table - and a query result is again a table - is unordered, we need an ORDER BY at last to get products first and categories second and also the products ordered alphabetically and the categories, too.
The complete query:
select category_or_product, sales_2021, sales_2022
from
(
select
product_code as category_or_product,
sum(case when year = 2021 then amount else 0 end) as sales_2021,
sum(case when year = 2021 then amount else 0 end) as sales_2022,
1 as product_first
from product
group by product_code
union all
select
category_code as category_or_product,
sum(case when year = 2021 then amount else 0 end) as sales_2021,
sum(case when year = 2021 then amount else 0 end) as sales_2022,
2 as product_first
from category
group by category_code
) unioned
order by product_first, category_or_product;

Combine and fuse the results of two SQL queries with UNION

I am writing two seperate SQL queries to get data for two different dates like so:
SELECT number, sum(sales) as sales, sum(discount) sa discount, sum(margin) as margin
FROM table_a
WHERE day = '2019-08-09'
GROUP BY number
SELECT number, sum(sales) as sales, sum(discount) sa discount, sum(margin) as margin
FROM table_a
WHERE day = '2018-08-10'
GROUP BY number
I tried fusing them like so to get the results for the same number in one row from two different dates:
SELECT number, sum(sales) as sales, sum(discount) sa discount, sum(margin) as margin, 0 as sales_n1, 0 as discount_n1, 0 as margin_n1
FROM table_a
WHERE day = '2019-08-09'
GROUP BY number
UNION
SELECT number, 0 as sales, 0 as discount, 0 as margin, sum(sales_n1) as sales_n1, sum(discount_n1) as discount_n1, sum(margin_n1) as margin_n1
FROM table_a
WHERE day = '2018-08-10'
GROUP BY number
But it didn't work as I get the rows for the first query with zeroes for the columns defined as zero followed by the columns of the second query in the same fashion.
How can I correct this to have the desired output ?
Use conditional aggregation:
SELECT number,
sum(case when day = '2019-08-09' then sales end) as sales_20190809,
sum(case when day = '2019-08-09' then discount end) sa discount, sum(margin) as margin_20190810,
sum(case when day = '2019-08-10' then sales end) as sales_20190809,
sum(case when day = '2019-08-10' then discount end) sa discount, sum(margin) as margin_20190810
FROM table_a
WHERE day IN ('2019-08-09', '2019-08-10')
GROUP BY number;
If you want the numbers in different rows (which you don't seem to), then use aggregation:
SELECT day, number, sum(sales) as sales, sum(discount) as discount, sum(margin) as margin
FROM table_a
WHERE day IN ('2019-08-09', '2019-08-10')
GROUP BY day, number

How can I add cumulative sum column?

I use SqlExpress
Following is the query using which I get the attached result.
SELECT ReceiptId, Date, Amount, Fine, [Transaction]
FROM (
SELECT ReceiptId, Date, Amount, 'DR' AS [Transaction]
FROM ReceiptCRDR
WHERE (Amount > 0)
UNION ALL
SELECT ReceiptId, Date, Amount, 'CR' AS [Transaction]
FROM ReceiptCR
WHERE (Amount > 0)
UNION ALL
SELECT strInvoiceNo AS ReceiptId, CONVERT(datetime, dtInvoiceDt, 103) AS Date, floatTotal AS Amount, 'DR' AS [Transaction]
FROM tblSellDetails
) AS t
ORDER BY Date
Result
want a new column which would show balance amount.
For example. 1 Row should show -2500, 2nd should -3900, 3rd should -700 and so on.
basically, it requires previous row' Account column's data and carry out calculation based on transaction type.
Sample Result
Well, that looks like SQL-Server , if you are using 2012+ , then use SUM() OVER() :
SELECT t.*,
SUM(CASE WHEN t.transactionType = 'DR'
THEN t.amount*-1
ELSE t.amount END)
OVER(PARTITION BY t.date ORDER BY t.receiptId,t.TransactionType DESC) as Cumulative_Col
FROM (YourQuery Here) t
This will SUM the value when its CR and the value*-1 when its DR
Right now I grouped by date, meaning each day will recalculate this column, if you want it for all time, replace the OVER() with this:
OVER(ORDER BY t.date,t.receiptId,t.TransactionType DESC) as Cumulative_Col
Also, I didn't understand why in the same date, for the same ReceiptId DR is calculated before CR , I've add it to the order by but if thats not what you want then explain the logic better.

Is it possible to have 1 query instead of 2 queries for the below mentioned case?

I have two tables in Postgresql as below:
receipt
id CHARACTER VARYING(20),
account CHARACTER VARYING(20),
date DATE,
amount NUMERIC(8,2),
PRIMARY KEY(id)
non_cash
id CHARACTER VARYING(20),
account CHARACTER VARYING(20),
date DATE,
amount NUMERIC(8,2),
PRIMARY KEY(id)
Now I want to select the SUM(amount) from both the tables where account = '00210_pre' and SUM(amount) and MAX(date) from both the tables where account = '00210_int'. I am able to get the desired result using 2 queries as:
SELECT SUM(amount) AS int_total, MAX(date)
FROM (SELECT amount, date, account FROM non_cash
UNION SELECT amount, date, account FROM receipts) AS v
WHERE account = '00210_int'
and
SELECT SUM(amount) AS pre_total
FROM (SELECT amount, account FROM non_cash
UNION SELECT amount, account FROM receipts) AS v
WHERE account = '00210_pre'
Now My question is Is it possible to have the above in just one query? If yes how?
Kindly note that the account only varies in the suffix as _pre and _int the rest numbers are same.
You shouldn't use UNION. You should use UNION ALL, because UNION removes duplicates, which is probably not desirable and is a waste of effort. But, the answer to your question is GROUP BY:
SELECT account, SUM(amount) AS int_total, MAX(date)
FROM ((SELECT amount, date, account FROM non_cash
) UNION ALL
(SELECT amount, date, account FROM receipts)
) v
WHERE account IN ('00210_int', '00210_pre')
GROUP BY account;
Note: if you want the values combined, then don't use the GROUP BY:
SELECT SUM(amount) AS int_total, MAX(date)
FROM ((SELECT amount, date, account FROM non_cash
) UNION ALL
(SELECT amount, date, account FROM receipts)
) v
WHERE account IN ('00210_int', '00210_pre');
EDIT:
To get a single row use conditional aggregation:
SELECT SUM(CASE WHEN account = '00210_int' THEN amount ELSE 0 END) AS int_total,
SUM(CASE WHEN account = '00210_pre' THEN amount ELSE 0 END) AS pre_total,
MAX(date)
FROM ((SELECT amount, date, account FROM non_cash
) UNION ALL
(SELECT amount, date, account FROM receipts)
) v
WHERE account IN ('00210_int', '00210_pre');
SELECT left(nc.account,5) account,
max(greatest(nc.date, r.date)) max_date,
sum(nc.amount + r.amount) total
sum(CASE WHEN nc.account = '00210_int' OR r.account = '00210_int' THEN
nc.amount + r.amount
ELSE NULL
END
) int_total,
sum(CASE WHEN nc.account = '00210_pre' OR r.account = '00210_pre' THEN
nc.amount + r.amount
ELSE NULL
END
) pre_total
FROM non_cash nc
INNER JOIN receipts r
ON left(nc.account,5) = left(r.account,5)
WHERE nc.account IN ( '00210_int', '00210_pre')
OR r.account IN ( '00210_int', '00210_pre')
GROUP BY 1

Can we use same aggregate function more than once on same table field or column using Different filter conditions?

I want to use SUM() function on 'Amount' field in a query 4 times on a same field with different filters.
something like
SELECT Date1,CC,BU, SUM(Amount),SUM(Amount),SUM(Amount),SUM(Amount)
FROM MainTable<br>
GROUP BY CC,BU,Date1
Here
1st SUM(Amount) should be calculated when Mode='011' And Mode='012' from MainTable
2nd SUM(Amount) should be calculated when Mode like '03_' And Mode Like '05_' from MainTable
3rd SUM(Amount) should be calculated when Mode like '10_' from MainTable
4th SUM(Amount) should be calculated when (Mode !='011') and (Mode !='012') (Mode not Like '03_') and (Mode not Like '05_') from MainTable
How to make this happen? I have tried in many ways but couldn't get the result the way I wanted.
Please help me.
Thank you in advance.
You can use an aggregate function with a CASE:
SELECT Date1,
CC,
BU,
SUM(case when mode = '011' then Amount end) Mode011,
SUM(case when mode = '012' then Amount end) Mode012,
SUM(case when mode = '013' then Amount end) Mode013,
SUM(case when mode = '014' then Amount end) Mode014
FROM MainTable
GROUP BY CC,BU,Date1;
Or you can use the PIVOT function:
select date1, CC, BU,
[011] Mode011,
[012] Mode012,
[013] Mode013,
[014] Mode014
from
(
select date1, CC, BU, mode, amount
from maintable
) src
pivot
(
sum(amount)
for mode in ([011], [012], [013], [014])
) piv