Cumulative sum across two tables - sql

I have two tables for Bill & Payment. I have show the balance sheet from these two tables.
The Data in the tables are:
tblBill
tblPayment
My current output is:
The query I'm trying to use is:
select Particulars,Date,BillAmount,0'PaidAmount' from tblBill
union
select Particulars,Date,0'BillAmount',PaidAmount from tblPayment
order by Date
However, I need my output in this format:
Is it possible to get the required format?

There you go:
Assuming there is only one transaction in a day....
With Tb1 as
(select Date,Particulars,BillAmount,0'PaidAmount' from tblBill
union
select Date,Particulars,0'BillAmount',PaidAmount from tblPayment
)
SELECT T1.Particulars,T1.[Date],T1.[BillAmount],T1.[PaidAmount],(Sum(T2.BillAmount) - Sum(T2.PaidAmount)) as Balance FROM Tb1 as T1
INNER JOIN
Tb1 as T2
ON T1.[date] >= T2.[date]
Group By T1.Particulars,T1.[Date],T1.[BillAmount],T1.[PaidAmount]
Order by [Date]
In case of more than one transactions in a day....
WITH Tb0 as
( SELECT [Date],Particulars,BillAmount,0'PaidAmount' from tblBill
UNION
SELECT [Date],Particulars,0'BillAmount',PaidAmount from tblPayment
)
, Tb1 as
(
SELECT Date,Particulars,BillAmount,PaidAmount,Row_Number() over (order by [Date] asc) as [OrderId]
FROM
Tb0
)
SELECT T1.Particulars,T1.[Date],T1.[BillAmount],T1.[PaidAmount],(Sum(T2.BillAmount) - Sum(T2.PaidAmount)) as Balance FROM Tb1 as T1
INNER JOIN
Tb1 as T2
ON T1.[OrderId] >= T2.[OrderId]
Group By T1.Particulars,T1.[Date],T1.[BillAmount],T1.[PaidAmount]
Order by [Date]

You will need to JOIN the two tables. First, there must be a link between the two tables (say customerid existing to show which customer is involved).
Then, you can do.
CREATE VIEW vwTransactionHistory as
SELECT customerid, Particulars, [DATE], BillAmount, PaidAmount,
(SELECT SUM(BillAmount) FROM tblBill x WHERE x.customerid=temp1.customerid and x.date<=temp1.date) as bill2date, (SELECT SUM(PaidAmount) FROM tblPayment y WHERE y.customerid = temp1.customerid and y.date<=temp1.date) as Pay2Date
FROM
(
select customerid, Particulars,[Date],BillAmount,0 AS 'PaidAmount' from tblBill
union
select customerid,Particulars,[Date],0 AS 'BillAmount',PaidAmount from tblPayment
) AS temp1
GROUP BY customerid, Particulars,[Date],BillAmount,PaidAmount
Then you can do
SELECT TOP 1000 [customerid]
,[Particulars]
,[DATE]
,[BillAmount],[PaidAmount], isnull(bill2date,0) - isnull(pay2date,0) as Balance
FROM [vwTransactionHistory]
Remember that you don't need to create a View. I use views for clarity and abstraction of complex Queries.

Check this query
select * from
(
select Particulars,Date,BillAmount,0'PaidAmount' , BillAmount as Balance from tblBill
union
select Particulars,Date,0'BillAmount',PaidAmount, BillAmount - PaidAmount as Balance
from tblPayment p
inner join tblBill b where p.Particulars = p.Particulars
) a
order by Date

You can use this query
SELECT Particulars,Date,BillAmount,PaidAmount,BillAmount-PaidAmount as Balance
FROM(
select Particulars,Date,BillAmount,0'PaidAmount' from tblBill
union
select Particulars,Date,0'BillAmount',PaidAmount from tblPayment
order by Date
)
ORDER BY Date;

Related

Can I nest a select statement within an IF function in SQL?

Using Teradata..
I want to write a query that joins table 1 and table 2 on item code to the location in table 2.
There are multiple locations per item code and potentially multiple item code entries per location depending on date. I'm only interested in the most recent item per location. To achieve this I've used a nested query to select the max date per both location and item number. I'm still returning more rows of data than anticipated and suspect it is due to some duplicate locations slipping through, potentially with two different item numbers.
I'm wondering if its possible to use the IF operator to say "If there are duplicate locations, choose the location with the more recent date"
Is this possible?
Here is what I have written so far:
SELECT t1.item_no, t1.date, t2.location, t2.date
FROM table 1 t1
JOIN table 2 t2 ON t1.item_no = t2.item_no
WHERE (t1.item_no, t1.date) IN
(
SELECT item_no, MAX(date)
FROM table 1
GROUP BY item_no
)
AND (t2.location, t2.date) IN
(
SELECT location, MAX(date)
FROM table 2
GROUP BY location
)
Change your query and use Subquery
SELECT t1.item_no, t1.date, t2.location, t2.date FROM
(
SELECT item_no, MAX(date) date
FROM table 1
GROUP BY item_no
) T1
JOIN
(
SELECT location, MAX(date) date
FROM table 2
GROUP BY location
) T2
ON t1.item_no = t2.location
Without knowing DBMS, a solution could be to use ROW_NUMBER(). I'm not sure if there's a preference for nested queries over say CTE but a solution w/ CTE could be:
WITH items AS (
SELECT
item_no
,date AS item_date
,row_number() OVER (PARTITION BY item_no ORDER BY date desc) as rn
FROM table1
),
locations AS (
SELECT
location
,item_no
,date AS location_date
,ROW_NUMBER() OVER(PARTITION BY item_no, location ORDER BY date desc) as rn
from table2
)
SELECT
t1.item_no
,t1.item_date
,t2.location
,t2.location_date
FROM items AS t1
JOIN locations AS t2 on t1.item_no = t2.item_no
AND t2.rn = 1
WHERE t1.rn = 1

Getting MAX datetime event from multiple tables, and outputing a simple list of most recent events by ID

I have a table:
and multiple other tables - consider them purchases, in this example:
And would like an output table to show the most recent purchase (NB that there may be multiple instances of a purchase within each table), by id from the main table:
The id can be a customer number, for example.
I've tried using OUTER APPLY on each purchase table, getting the TOP 1 by datetime desc, then getting the max value from the OUTER APPLY tables, but I would not get the table name - eg. Apples, just the datetime.
Another idea was to UNION all of the purchase tables together in a join with the main table (by id), and pick out the top 1 datetime and a table name, but I don't think this would be very efficient for a lot of rows:
SELECT MT.id, MT.gender, MT.age,
b.Name as LastPurchase, b.dt as LastPurchaseDateTime
FROM MainTable MT
LEFT JOIN (
SELECT id, Name, MAX(dt) FROM
(
SELECT id, 'Apples' as Name, ApplesDateTime as dt FROM ApplesTable
UNION
SELECT id, 'Pears' as Name, PearsDateTime as dt FROM PearsTable
UNION
SELECT id, 'Bananas' as Name, BananasDateTime as dt FROM BananasTable
)a
GROUP BY etc
)b
Does anyone have a more sensible idea?
Many thanks in advance.
I would go for a lateral join:
select m.*, x.*
from maintable m
outer apply (
select top (1) x.*
from (
select id, 'apples' as name, applesdatetime as dt from applestable
union all select id, 'pears', pearsdatetime from pearstable
union all select id, 'bananas', bananasdatetime from bananastable
) x
where x.id = m.id
order by dt desc
) x
I would suggest apply:
SELECT MT.id, mt.gender, mt.age, p.*
FROM MainTable MT OUTER APPLY
(SELECT p.name, p.dt
FROM (SELECT id, 'Apples' as Name, ApplesDateTime as dt FROM ApplesTable
UNION ALL
SELECT id, 'Pears' as Name, PearsDateTime as dt FROM PearsTable
UNION ALL
SELECT id, 'Bananas' as Name, BananasDateTime as dt FROM BananasTable
) p
WHERE p.id = mt.id
ORDER BY dt DESC
) p

update all rows of a table based on minimum value of its group

I have a table like this
Date----- ----------Value--------- Group <br>
2017-01-01--------10--------------1--<br>
2017-01-02---------9---------------1--<br>
2017-01-03 --------5---------------2--<br>
2017-01-04 --------4---------------2--<br>
i want to update all value column in the table such that it is set to minimum date's value in that group
like this
Date----- ----------Value--------- Group <br>
2017-01-01--------10--------------1--<br>
2017-01-02---------10---------------1--<br>
2017-01-03 --------5---------------2--<br>
2017-01-04 --------5---------------2--<br>
Here you go, 2 sub-queries, the first to calculate min date per group then join back to original table to get the associated value. Then finally join this to the original table to update all associated groups with that value:
UPDATE M SET M.Value = RESULT.Value FROM MyTable M
INNER JOIN (
SELECT MV.Group, M.Value FROM MyTable M
INNER JOIN (
SELECT MIN(Date) as MinDateValue, Group FROM MyTable
GROUP BY Group
) MV ON MV.MinDateValue = M.Date AND MV.Group = M.Group
) RESULT ON RESULT.Group = M.Group
First get min date and value from sub query.Based on this result update main table
CREATE TABLE #Table(_Date Date,value INT,_Group INT)
INSERT INTO #Table(_Date ,value ,_Group)
SELECT '2017-01-01',10,1 UNION ALL
SELECT '2017-01-02',9,1 UNION ALL
SELECT '2017-01-03',5,2 UNION ALL
SELECT '2017-01-04',4,2
UPDATE #Table SET value = _Output._Value
FROM
(
SELECT A._Date , A._Group , T.value _Value
FROM #Table T
JOIN
(
SELECT MIN(_Date) _Date ,_Group
FROM #Table
GROUP BY _Group
) A ON A._Date = T._Date
) _Output WHERE _Output._Group = #Table._Group
SELECT * FROM #Table
You can also use a CTE.
Query
;with cte as(
select [rn] = row_number() over(
partition by [Group]
order by [Date]
), *
from [your_table_name]
)
update t1
set t1.[Value] = t2.[Value]
from cte t1
join cte t2
on t1.[Group] = t2.[Group]
and t1.[rn] > t2.[rn];

SQL SUM function inquiry

I'm having a hard time summing up a column on two tables. The scenario is something like this (refer to the image below)
Table 1 may have a lot of rows per Date. But Table 2 may only consists of two rows of data per Date. What I wanted to do is to sum up all Item/Price (Table1) according to their Date and ADD them with another SUM of Item/Price of Table2. The category of SUM is by Date.
I tried any joins statement (left, right or inner) but it does not produce the result that I am expecting to. My expected result is the Result table. But on my query, it produces a very high value.
Thanks.
Use a UNION clause like this:
WITH t(d, p) AS (
SELECT [Date], Price FROM Table1
UNION ALL
SELECT [Date], Price FROM Table2
)
SELECT d, SUM(p) FROM t GROUP BY d
You can do this with UNION ALL in either a subquery or a cte, cte shown here:
;WITH cte AS (SELECT [Date], Price
FROM Table1
UNION ALL
SELECT [Date], Price
FROM Table2
)
SELECT [Date], SUM(Price) AS Total_Price
FROM cte
GROUP BY [Date]
Demo: SQL Fiddle
Try This,
with cte (C_Date,C_Price)
as
(
SELECT date,SUM(price) FROM table_1
group by date
union
SELECT date,SUM(price) FROM table_2
group by date
)
select c_date,SUM(c_price) from cte
group by C_Date
Try this
Select t.date,P1+P2
from(
Select Date,sum(Price) P1
from table1 t
group by Date
) t
left join
(
Select Date,sum(Price) P2
from table t2
group by date
) t1 on t.date = t1.date
group by date

How to get Original Rows filtered by a HAVING Condition?

What is the method in T-SQL to select the orginal values limited by a HAVING attribute. For example, if I have
A|B
10|1
11|2
10|3
How would I get all the values of B (Not An Average or some other summary stat), Grouped by A, having a Count (Occurrences of A) greater than or equal two 2?
Actually, you have several options to choose from
1. You could make a subquery out of your original having statement and join it back to your table
SELECT *
FROM YourTable yt
INNER JOIN (
SELECT A
FROM YourTable
GROUP BY
A
HAVING COUNT(*) >= 2
) cnt ON cnt.A = yt.A
2. another equivalent solution would be to use a WITH clause
;WITH cnt AS (
SELECT A
FROM YourTable
GROUP BY
A
HAVING COUNT(*) >= 2
)
SELECT *
FROM YourTable yt
INNER JOIN cnt ON cnt.A = yt.A
3. or you could use an IN statement
SELECT *
FROM YourTable yt
WHERE A IN (SELECT A FROM YourTable GROUP BY A HAVING COUNT(*) >= 2)
A self join will work:
select B
from table
join(
select A
from table
group by 1
having count(1)>1
)s
using(A);
You can use window function (no joins, only one table scan):
select * from (
select *, cnt=count(*) over(partiton by A) from table
) as a
where cnt >= 2