Querying SQL Server to obtain sum total using CTE and joins - sql-server

I have a below query that I am trying since yesterday with some 33 records of Employee with employeeId on various conditions:
With CTE
(
select EmployeeId, and other colums with joins and conditions.
)
Now I want to join this query to obtain sum of invoices of each employee from below tables, table1 and table2.
table1 has employeeid so as my CTE has employeeid I can join it with table1
With CTE
(
select EmployeeId, and other colums with joins and conditions.
)
select *, table1.invoiceId
from CTE
left join table1 on table1.employeeid = CTE.employeeId
left join table2 on table2.invoiceid = table1.invoiceid
groupby
But my table1 only have invoices and for each such invoice there are amount spend in other table i.e table2. table2 has column "amount" that I need to sum up depending upon invoiceid.
For more clarity I am writing the table structure or output as below.
I am trying like above but they are not showing correct results
Assume CTE has
Emplyeeid empName Empaddress empcode
1 john America 121
2 sandy America 122
Now table1 has
InvoiceId EmployeeId RecordId PAyeeid
1 1 223 202
2 1 222 212
3 1 121 378
4 2 229 987
5 2 345 333
table2 has the coulmm amount that we need for each invoice of epmloyee
now table2
InvLine Invoiceid Amount
1 1 30
2 1 30
3 1 20
4 2 10
5 2 10
6 2 10
The output should be as per employe john has two invoices in table1 ie with Id 1 and 2, and for 1 and 2 invoiceds there are amounts that need to be add up
Emplyeeid empName Empaddress empcode Amount
1 john America 121 80

With CTE
(
select EmployeeId, and other colums with joins and conditions.
)
With CTE1
(
select EmployeeId,table1.invoiceid
from cte
left join table1 on table1.employeeid=CTE.employeeId
)
select sum(amount), cte1.employeeId from CTE1
left join table2 on table2.invoiceid = cte1.invoiceid
group by cte1.employeeId
but you can join the table1 in the first cte itself. There is no need to go for second cte if the first cte is simple one.

Related

Sqlserver minus with unmatched columns

I have two tables with different columns and want intersect records.
Table1:
Id name age
1 AAA 20
2 AAA 30
3 BBB 25
4 BBB 30
Table2:
name age
AAA 20
BBB 30
Expect Output:(Table2 - Table1)
Id name age
2 AAA 30
3 BBB 25
You can use NOT EXISTS with a correlated subquery to check that no tuple (name, age) with identical values exists.
SELECT *
FROM table1 t1
WHERE NOT EXISTS (SELECT *
FROM table2 t2
WHERE t2.name = t1.name
AND t2.age = t1.age);
Use Right Join
SELECT DISTINCT Table1.Id, Table1.Name, Table1.Age
FROM Table2 RIGHT JOIN Table1 ON Table1.Name = Table2.Name AND Table1.Age = Table2.Age
WHERE Table2.Name IS NULL
FIDDLE DEMO

Joining Two Tables, getting Aggregate and unique value of one

This may have been answered previously, but I'm having a difficult time describing my issue.
Let's say I have two tables
Table1
User, CalendarID
Joe 1
Joe 2
Joe 3
Sam 4
Bob 1
Jim 2
Jim 3
Table2
CalendarID, CalendarTime
1 2014-08-18 00:00:00.000
2 2015-01-19 00:00:00.000
3 2015-08-24 00:00:00.000
4 2016-01-18 00:00:00.000
What I would like to do is Join the two tables, only getting a single User Name, and Calendar ID based on what is this highest CalendarTime associated with that CalandarID.
So I would like the query to return
User CalendarID
Joe 3
Sam 4
Bob 1
Jim 3
The closest I've managed is
SELECT t1.User, MAX(t2.CalendarTIme) AS CalendarTime
FROM table1 t1
INNER JOIN table2 as t2
ON t1.CalendarID = t2.CalendarID
Group By t1.User
Which gets me the User and CalendarTime that I want, but not the Calendar ID, which is what I really want. Please help.
Closest to your script and pretty straightforward:
SELECT t1.User, t2.*
FROM table1 t1
INNER JOIN table2 as t2
ON t1.CalendarID = t2.CalendarID
WHERE NOT EXISTS
(
SELECT 1 FROM table1 t1_2
INNER JOIN table2 t2_2
ON t2_2.Calendar_ID = t1_2.Calendar_ID
WHERE t1_2.User = t1.User
AND t2_2.CalendarTime > t2.CalendarTime
)
This can be solved for the top N per group:
using top with ties with row_number():
select top 1 with ties
t1.User, t1.CalendarId, t2.CalendarTime
from table1 t1
inner join table2 as t2
on t1.Calendarid = t2.Calendarid
order by row_number() over (partition by t1.User order by t2.CalendarTime desc)
or using common table expression(or a derived table/subquery) with row_number()
;with cte as (
select t1.User, t1.CalendarId, t2.CalendarTime
, rn = row_number() over (partition by t1.User order by t2.CalendarTime desc)
from table1 t1
inner join table2 as t2
on t1.Calendarid = t2.Calendarid
)
select User, CalendarId, CalendarTime
from cte
where rn = 1

How to find the cumulative sum in SubQuery? [duplicate]

declare #t table
(
id int,
SomeNumt int
)
insert into #t
select 1,10
union
select 2,12
union
select 3,3
union
select 4,15
union
select 5,23
select * from #t
the above select returns me the following.
id SomeNumt
1 10
2 12
3 3
4 15
5 23
How do I get the following:
id srome CumSrome
1 10 10
2 12 22
3 3 25
4 15 40
5 23 63
select t1.id, t1.SomeNumt, SUM(t2.SomeNumt) as sum
from #t t1
inner join #t t2 on t1.id >= t2.id
group by t1.id, t1.SomeNumt
order by t1.id
SQL Fiddle example
Output
| ID | SOMENUMT | SUM |
-----------------------
| 1 | 10 | 10 |
| 2 | 12 | 22 |
| 3 | 3 | 25 |
| 4 | 15 | 40 |
| 5 | 23 | 63 |
Edit: this is a generalized solution that will work across most db platforms. When there is a better solution available for your specific platform (e.g., gareth's), use it!
The latest version of SQL Server (2012) permits the following.
SELECT
RowID,
Col1,
SUM(Col1) OVER(ORDER BY RowId ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS Col2
FROM tablehh
ORDER BY RowId
or
SELECT
GroupID,
RowID,
Col1,
SUM(Col1) OVER(PARTITION BY GroupID ORDER BY RowId ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS Col2
FROM tablehh
ORDER BY RowId
This is even faster. Partitioned version completes in 34 seconds over 5 million rows for me.
Thanks to Peso, who commented on the SQL Team thread referred to in another answer.
For SQL Server 2012 onwards it could be easy:
SELECT id, SomeNumt, sum(SomeNumt) OVER (ORDER BY id) as CumSrome FROM #t
because ORDER BY clause for SUM by default means RANGE UNBOUNDED PRECEDING AND CURRENT ROW for window frame ("General Remarks" at https://msdn.microsoft.com/en-us/library/ms189461.aspx)
Let's first create a table with dummy data:
Create Table CUMULATIVESUM (id tinyint , SomeValue tinyint)
Now let's insert some data into the table;
Insert Into CUMULATIVESUM
Select 1, 10 union
Select 2, 2 union
Select 3, 6 union
Select 4, 10
Here I am joining same table (self joining)
Select c1.ID, c1.SomeValue, c2.SomeValue
From CumulativeSum c1, CumulativeSum c2
Where c1.id >= c2.ID
Order By c1.id Asc
Result:
ID SomeValue SomeValue
-------------------------
1 10 10
2 2 10
2 2 2
3 6 10
3 6 2
3 6 6
4 10 10
4 10 2
4 10 6
4 10 10
Here we go now just sum the Somevalue of t2 and we`ll get the answer:
Select c1.ID, c1.SomeValue, Sum(c2.SomeValue) CumulativeSumValue
From CumulativeSum c1, CumulativeSum c2
Where c1.id >= c2.ID
Group By c1.ID, c1.SomeValue
Order By c1.id Asc
For SQL Server 2012 and above (much better performance):
Select
c1.ID, c1.SomeValue,
Sum (SomeValue) Over (Order By c1.ID )
From CumulativeSum c1
Order By c1.id Asc
Desired result:
ID SomeValue CumlativeSumValue
---------------------------------
1 10 10
2 2 12
3 6 18
4 10 28
Drop Table CumulativeSum
A CTE version, just for fun:
;
WITH abcd
AS ( SELECT id
,SomeNumt
,SomeNumt AS MySum
FROM #t
WHERE id = 1
UNION ALL
SELECT t.id
,t.SomeNumt
,t.SomeNumt + a.MySum AS MySum
FROM #t AS t
JOIN abcd AS a ON a.id = t.id - 1
)
SELECT * FROM abcd
OPTION ( MAXRECURSION 1000 ) -- limit recursion here, or 0 for no limit.
Returns:
id SomeNumt MySum
----------- ----------- -----------
1 10 10
2 12 22
3 3 25
4 15 40
5 23 63
Late answer but showing one more possibility...
Cumulative Sum generation can be more optimized with the CROSS APPLY logic.
Works better than the INNER JOIN & OVER Clause when analyzed the actual query plan ...
/* Create table & populate data */
IF OBJECT_ID('tempdb..#TMP') IS NOT NULL
DROP TABLE #TMP
SELECT * INTO #TMP
FROM (
SELECT 1 AS id
UNION
SELECT 2 AS id
UNION
SELECT 3 AS id
UNION
SELECT 4 AS id
UNION
SELECT 5 AS id
) Tab
/* Using CROSS APPLY
Query cost relative to the batch 17%
*/
SELECT T1.id,
T2.CumSum
FROM #TMP T1
CROSS APPLY (
SELECT SUM(T2.id) AS CumSum
FROM #TMP T2
WHERE T1.id >= T2.id
) T2
/* Using INNER JOIN
Query cost relative to the batch 46%
*/
SELECT T1.id,
SUM(T2.id) CumSum
FROM #TMP T1
INNER JOIN #TMP T2
ON T1.id > = T2.id
GROUP BY T1.id
/* Using OVER clause
Query cost relative to the batch 37%
*/
SELECT T1.id,
SUM(T1.id) OVER( PARTITION BY id)
FROM #TMP T1
Output:-
id CumSum
------- -------
1 1
2 3
3 6
4 10
5 15
Select
*,
(Select Sum(SOMENUMT)
From #t S
Where S.id <= M.id)
From #t M
You can use this simple query for progressive calculation :
select
id
,SomeNumt
,sum(SomeNumt) over(order by id ROWS between UNBOUNDED PRECEDING and CURRENT ROW) as CumSrome
from #t
There is a much faster CTE implementation available in this excellent post:
http://weblogs.sqlteam.com/mladenp/archive/2009/07/28/SQL-Server-2005-Fast-Running-Totals.aspx
The problem in this thread can be expressed like this:
DECLARE #RT INT
SELECT #RT = 0
;
WITH abcd
AS ( SELECT TOP 100 percent
id
,SomeNumt
,MySum
order by id
)
update abcd
set #RT = MySum = #RT + SomeNumt
output inserted.*
For Ex: IF you have a table with two columns one is ID and second is number and wants to find out the cumulative sum.
SELECT ID,Number,SUM(Number)OVER(ORDER BY ID) FROM T
Once the table is created -
select
A.id, A.SomeNumt, SUM(B.SomeNumt) as sum
from #t A, #t B where A.id >= B.id
group by A.id, A.SomeNumt
order by A.id
The SQL solution wich combines "ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW" and "SUM" did exactly what i wanted to achieve.
Thank you so much!
If it can help anyone, here was my case. I wanted to cumulate +1 in a column whenever a maker is found as "Some Maker" (example). If not, no increment but show previous increment result.
So this piece of SQL:
SUM( CASE [rmaker] WHEN 'Some Maker' THEN 1 ELSE 0 END)
OVER
(PARTITION BY UserID ORDER BY UserID,[rrank] ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS Cumul_CNT
Allowed me to get something like this:
User 1 Rank1 MakerA 0
User 1 Rank2 MakerB 0
User 1 Rank3 Some Maker 1
User 1 Rank4 Some Maker 2
User 1 Rank5 MakerC 2
User 1 Rank6 Some Maker 3
User 2 Rank1 MakerA 0
User 2 Rank2 SomeMaker 1
Explanation of above: It starts the count of "some maker" with 0, Some Maker is found and we do +1. For User 1, MakerC is found so we dont do +1 but instead vertical count of Some Maker is stuck to 2 until next row.
Partitioning is by User so when we change user, cumulative count is back to zero.
I am at work, I dont want any merit on this answer, just say thank you and show my example in case someone is in the same situation. I was trying to combine SUM and PARTITION but the amazing syntax "ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW" completed the task.
Thanks!
Groaker
Above (Pre-SQL12) we see examples like this:-
SELECT
T1.id, SUM(T2.id) AS CumSum
FROM
#TMP T1
JOIN #TMP T2 ON T2.id < = T1.id
GROUP BY
T1.id
More efficient...
SELECT
T1.id, SUM(T2.id) + T1.id AS CumSum
FROM
#TMP T1
JOIN #TMP T2 ON T2.id < T1.id
GROUP BY
T1.id
Try this
select
t.id,
t.SomeNumt,
sum(t.SomeNumt) Over (Order by t.id asc Rows Between Unbounded Preceding and Current Row) as cum
from
#t t
group by
t.id,
t.SomeNumt
order by
t.id asc;
Try this:
CREATE TABLE #t(
[name] varchar NULL,
[val] [int] NULL,
[ID] [int] NULL
) ON [PRIMARY]
insert into #t (id,name,val) values
(1,'A',10), (2,'B',20), (3,'C',30)
select t1.id, t1.val, SUM(t2.val) as cumSum
from #t t1 inner join #t t2 on t1.id >= t2.id
group by t1.id, t1.val order by t1.id
Without using any type of JOIN cumulative salary for a person fetch by using follow query:
SELECT * , (
SELECT SUM( salary )
FROM `abc` AS table1
WHERE table1.ID <= `abc`.ID
AND table1.name = `abc`.Name
) AS cum
FROM `abc`
ORDER BY Name

Select primary key where max value in group clase SQL Server

How I can do select a primary key in grouping by clause, and it will return max value from another table which not in group by clause? For example :
Table A :
ID table_b_id Value
1 1 100
2 1 200
3 1 150
4 2 300
5 2 200
6 2 100
7 3 100
8 3 200
Table B
ID Name
1 A
2 B
3 C
Result Expected
B.ID B.Name A.ID
1 A 2
2 B 4
3 C 8
I've try some queries like this :
select b.id, max(b.name), max(a.id) as kd_rec
from table_a a join table_b
on a.table_b_id = b.id
group by b.id
I don't know how to get max value from table a group by b.
If you don't want the Name from tableB, then
Query
;with cte as
(
select rn=row_number() over
(
partition by table_b_id
order by [Value] desc
),*
from tableA
)
select table_b_id as [B.ID],
ID as [A.ID]
from cte
where rn=1;
Fiddle demo
If you want the Name also in the result set, then
Query
;with cte as
(
select rn=row_number() over
(
partition by table_b_id
order by [Value] desc
),*
from tableA
)
select t1.table_b_id as [B.ID],
t2.Name as [B.Name],
t1.ID as [A.ID]
from cte t1
join tableB t2
on t1.table_b_id=t2.ID
where t1.rn=1;
Fiddle demo

How to get sum of a column for three different tables without Foreign Key relationship

I have three tables with common id and different cost for each table.
I need to display the sum of cost1, cost2, cost3 for all unique id as single ResultSet.
table1:
id cost1
1 100
1 100
2 200
table2:
id cost2
1 100
2 100
2 100
table 3:
id cost3
1 100
2 100
1 100
The out should look like sum of the column in each table cost:
Outout:
id cost1 cost2 cost3
1 200 100 200
2 200 200 100
Could anyone suggest me the best solution for this.
Your question is not exactly clear but I think you are looking for something along these lines.
with costs1 as
(
select ID
, SUM(cost1) as cost1
from table1
group by ID
)
, costs2 as
(
select ID
, SUM(cost2) as cost2
from table2
group by ID
)
, costs3 as
(
select ID
, SUM(cost3) as cost3
from table3
group by ID
)
select c1.ID
, c1.cost1
, c2.cost2
, c3.cost3
from costs1 c1
join costs2 c2 on c2.ID = c1.ID
join costs3 c3 on c3.ID = c1.ID
A UNION ALL followed with a PIVOT will solve this more efficiently than a JOIN to each table.
SQL Fiddle Demo
WITH t1(id,costnum,cost) AS (
SELECT id,'cost1',cost1 FROM Table1 UNION ALL
SELECT id,'cost2',cost2 FROM Table2 UNION ALL
SELECT id,'cost3',cost3 FROM Table3
)
SELECT *
FROM t1
PIVOT(SUM(cost) FOR costnum IN ([cost1],[cost2],[cost3])) t2
we can achieve the same using UNION ALL and Derived table
select DISTINCT A.Id,
SUM(A.cost1),
SUM(A.cost2),
SUM(A.cost3) from (
select id,SUM(cost1)cost1,'' As Cost2,'' As cost3 from table1
GROUP BY t.id
UNION ALL
select id,'' As Cost1,SUM(cost2)cost2,'' As cost3 from table2
GROUP BY id
UNION ALL
select id,'' As Cost1,'' As cost2,SUM(t.cost3)cost3 from table3
GROUP BY id )A
GROUP BY A.Id

Resources