I'd like to either add a case statement to return Y/N for the 'All Complete?' column or exclude all rows where 'All complete' = 'Y'.
In order for 'All Complete' to be set to Y, all ID_Status's need to be complete for each customer and each code number. Each customer can have multiple ID's or Code_Number. I'm having trouble understanding CASE logic across multiple columns where values aren't always the same. Thanks for any help.
IDTable:
ID ID_Status Customer Code_Number All Complete?
1 Complete Alex 123 Y
2 Complete Alex 123 Y
3 Complete Brian 321 Y
4 Complete Brian 321 Y
5 Open Brian 425 N
6 Complete Brian 425 N
7 Open Charlie 123 N
8 Complete Charlie 123 N
9 Open Charlie 123 N
10 Complete Donald 555 N
11 Complete Donald 555 N
12 Testing Donald 555 N
13 Complete Eric 620 Y
Assuming SQL Server 2008+:
WITH CTE AS
(
SELECT *,
COUNT(*) OVER(PARTITION BY Customer, Code_Number) Total,
SUM(CASE WHEN Id_Status = 'Complete' THEN 1 ELSE 0 END) OVER(PARTITION BY Customer, Code_Number) Completed
FROM dbo.YourTable
)
SELECT ID,
ID_Status,
Customer,
Code_Number,
CASE WHEN Total = Completed THEN 'Y' ELSE 'N' END [All Complete]
FROM CTE;
Select *
, CASE WHEN EXISTS
(SELECT 1 FROM TableName t1
Where t1.Customer = t2.Customer
AND t1.Code_Number = t2.Code_Number
AND t1.ID_Status <> 'Complete'
)
THEN 'N' ELSE 'Y' END AS [All Complete?]
from TableName t2
Related
I have below sample input table. In real it has lots of records.
Input:
ID
Classification
123
1
123
2
123
3
123
4
657
1
657
3
657
4
For a 'ID', I want it's records should have 'Classification' column contains all the values 1, 2, 3 and 4. If any of these values are not present then that ID's records should be considered as an exception. The output should be as below.
ID
Classification
Flag
123
1
0
123
2
0
123
3
0
123
4
0
657
1
1
657
3
1
657
4
1
Can someone please help me with how can this can be achieved in sql server.
Thanks.
There are a couple of options here, which is more performant is up to you to test, not me (especially when I don't know what indexes you have). One uses conditional aggregation, to check that all the values are there, and the other uses a subquery and counts the DISTINCT values (as I don't know if there could be duplicate classifications):
SELECT *
INTO dbo.YourTable
FROM (VALUES(123,1),
(123,2),
(123,3),
(123,4),
(657,1),
(657,3),
(657,4))V(ID,Classification);
GO
CREATE CLUSTERED INDEX CI_YourIndex ON dbo.YourTable (ID,Classification);
GO
SELECT ID,
Classification,
CASE WHEN COUNT(CASE YT.Classification WHEN 1 THEN 1 END) OVER (PARTITION BY ID) > 0
AND COUNT(CASE YT.Classification WHEN 2 THEN 1 END) OVER (PARTITION BY ID) > 0
AND COUNT(CASE YT.Classification WHEN 3 THEN 1 END) OVER (PARTITION BY ID) > 0
AND COUNT(CASE YT.Classification WHEN 4 THEN 1 END) OVER (PARTITION BY ID) > 0 THEN 1 ELSE 0
END AS Flag
FROM dbo.YourTable YT;
GO
SELECT ID,
Classification,
CASE (SELECT COUNT(DISTINCT sq.Classification)
FROM dbo.YourTable sq
WHERE sq.ID = YT.ID
AND sq.Classification IN (1,2,3,4)) WHEN 4 THEN 1 ELSE 0
END AS Flag
FROM dbo.YourTable YT;
GO
DROP TABLE dbo.YourTable;
Problem:
We have a transaction table. All records in that table have one of the following transaction types: wallet deposit (payments), wallet withdrawal (sales) and cashback (discount to be used for future sales). I want to add a additional column to each row displaying the cashback balance. Cashback is either used for discount on new sales or to reduce negative overall balance.
transaction table:
customer (int)
transaction_date (date)
amount (int)
transaction_type (varchar(25))
I've tried using the lag function to get the value of the previous row and use that for the calculation in the current row. But that doesn't always work because the lag function looks back to the row it's specificly pointed to.
using lag function in calculation:
case when
isnull(lag(balance_cashback) over (partition by client_id order by transaction_date), 0)
+ case when type = "cashback" then amount else 0 end
+ case when type = "revenu" and amount < 0 then amount else 0 end
<= 0 then 0
else
lag(balance_cashback) over (partition by client_id order by transaction_date)
+ case when type = "cashback" then amount else 0 end
+ case when type = "revenu" and amount < 0 then amount else 0 end
end
Searching the internet I think I should be using a loop or maybe a cursor?
Idea:
The idea is to use the transaction table and add two rownumber columns. A rownumber for all rows in the transaction table I want to loop through. And a second rownumber on all transactions of each clients. The next step seems to create an empty balance table with fields rownumclient, client_id, overall_balance and cashback_balance.
calculation of rownumber colums:
row_number () over (order by client_id, transaction_date) as rownumber_all
row_number () over (partition by client_id order by client_id, transaction_date) as rownumber_client
transaction table with rownumbers:
rownumber_all (int)
rownumber_client (int)
client (int)
transaction_date (date)
amount (int)
transaction_type (varchar(25))
balance table:
rownumber_client (int)
client_id (int)
overall_balance (int)
cashback_balance (int)
example transaction table with rownumbers:
rownumbwr_all | rownumber_client | client_id | transaction_date | amount | transaction_type
1 1 123 2018-10-12 10 wallet deposit
2 2 123 2018-10-27 5 cashback
3 3 123 2018-11-03 -2,5 wallet withdrawal
4 4 123 2018-11-13 -5 wallet withdrawal
5 5 123 2018-12-18 10 wallet deposit
6 6 123 2018-12-19 20 wallet deposit
7 7 123 2018-12-21 5 cashback
8 1 456 2018-10-11 -45 wallet withdrawal
9 2 456 2018-10-23 5 cashback
10 3 456 2018-11-01 5 cashback
11 4 456 2018-11-04 10 wallet deposit
Etc.
With the additional rownumber columns and new balance table in place, I have to create a loop through all rows in the transaction table. Using the column rownumber_all to start with the first one. The newly created balance table is used to calculate the cashback balance in the current row. We use this table with a left join to the transaction tabel with the rownumber columns. When we loop through the first row, the balance table is empty, but from the second row on there is a calculated cashback balance from the previous row.
select statement for calculating current cashback balance:
select
t1.rownumall,
t1.rownumclient,
t1.client_id,
t2.overall_balance + t1.amount as overall_balance,
case
when (t2.overall_balance + case when t1.type = 'cashback' then t1.amount else 0 end) < 0 then 0
when t1.type in (sales, cashback) then amount
else null
end + t2.cashback_balance as cashback_balance
/*insert into balance*/
from
transactions as t1
left join cashback as t2 on t2.client_id = t1.client_id and t2.rownumber_client = t1.rownumber_client-1
For each row that is looped through the result of the select statement above should be inserted into the balance table as long as there are transaction records available. And as said before cashback balance is either used for discount on new sales or to reduce negative overall balance. That said the expected result I'm looking for is as follows and cashback_balance is the most important field.
expected transaction table with balances:
client_id | transaction_date | amount | transaction_type | overall_balance | cashback balance
123 2018-10-12 10 wallet deposit 10 0
123 2018-10-27 5 cashback 15 5
123 2018-11-03 -2,5 wallet withdrawal 12,5 2,5
123 2018-11-13 -5 wallet withdrawal 7,5 0
123 2018-12-18 10 wallet deposit 17,5 0
123 2018-12-19 20 wallet deposit 37,5 0
123 2018-12-21 5 cashback 42,5 5
456 2018-10-11 -45 wallet withdrawal -2,5 0
456 2018-10-23 5 cashback 2,5 2,5
456 2018-11-01 5 cashback 7,5 7,5
456 2018-11-04 10 wallet deposit 17,5 7,5
Etc.
I tried to explain as much as possible and hope the idea and expected result is clear. I can't imagine that what I need hasn't been done before, but I just can't seem to find the specific use case anywhere.
So which SQL expert will be kind enough to tell me in plain english how this can be achieved bu using a loop, cursor or any other way? Any help would be highly appreciated. If any clarification is needed, please let me know.
Are you looking for a running total like a bank statement. This query does that
SELECT
client_id,
Transaction_Date,
Trans_Type,
Amount,
Balance = SUM(CASE WHEN Trans_Type = 'Cash back' Then Amount ELSE 0 END) OVER(ORDER BY RowNumber)
FROM
(
SELECT
client_id,
Transaction_Date,
Trans_Type,
Amount,
ROW_NUMBER() OVER(ORDER BY Client_id) AS RowN
FROM temp.dbo.Trans_Table) RC
GROUP BY client_id, Trans_Type, Transaction_Date, Amount, RowN
Sample Data
After searching and some trial and error I've found a way to loop through all the rows and calculate the correct cashback balance for each row. I want to thank all the people who tried to help me and Jorge E. Hernández for the solution to my "loop problem".
Here the final code I used.
-- declare the start and end variable
declare
#counter int = 1,
#max_rownumber int = (select max(rownumber_all) as max_rownumber from dbo.transactions)
-- loop
while #counter <= #max_rownumber
begin
-- calculate overall_balance and cashback_balance for each row in the transactions table filtered by the rownumber_all field
insert into dbo.transactions_enriched
select
t1.rownumber_client as rownumber
, t1.client_id
, t1.transaction_date
, t1.amount
, t1.transaction_type
, t1.payment_method
, isnull(t2.overall_balance ,0) + t1.amount as overall_balance
, case
when t1.transaction_type = 'cashback' and isnull(t2.overall_balance, 0) >= 0 then isnull(t2.cashback_balance, 0) + t1.amount
when (case when t1.transaction_type = 'revenue' and t1.amount < 0 then t1.amount else 0 end) + isnull(t2.cashback_balance, 0) <= 0 then 0
else (case when t1.transaction_type = 'revenue' and t1.amount < 0 then t1.amount else 0 end) + isnull(t2.cashback_balance, 0)
end as cashback_balance
from
dbo.transactions as t1
left join dbo.transactions_enriched as t2 on t2.client_id = t1.client_id and t2.rownumber_client = t1.rownumber_client - 1
where
t1.rownumber_all = #counter
-- update the counter by adding 1
set #counter = #counter + 1
end
I have two tables.Department as follows
ID DName
1 IT
2 HR
3 Admin
Employee as follows
id fname departmentid
1 Mary 2
2 Rahul 2
3 Amit 3
4 Vivek 1
5 Preetam 1
6 Mangesh 1
7 Mary 1
Observe that there are two records for Mary (id 1 and 7) in employee table. Now I want to get result with name of employee and whether it works in HR department or not. Expected output is as follows.
fname WorksInHR
Mary Y
Rahul Y
Amit N
Vivek N
Preetam N
Mangesh n
How can i achieve this in SQL2012?
Here is one way of doing this:
select e.fname,
(case when sum(case when d.dname = 'HR' then 1 else 0 end) > 0
then 'Y' else 'N'
end) as WorksInHR
from employee e join
department d
on e.departmentid = d.id
group by e.fname;
Say I have a table let's call it purchase table in SQL Server that represents user purchasing.
Table name: purchase
purchase_id buyer_member_id song_id
1 101 1001
2 101 1002
3 102 1001
4 102 1003
5 103 1001
6 103 1003
7 103 1004
Now I tried to make some stats out of this table. I want to know who has purchased both song 1001 and 1003.
select distinct buyer_member_id from purchase where
buyer_member_id in (select buyer_member_id from purchase where song_id = 1001)
and buyer_member_id in (select buyer_member_id from purchase where song_id = 1003)
This works but when we add more and more criteria to the equation, it became slower and slower. It's nearly impossible to do a research for something like, find people who buy a, b and c but not d nor f. I understand that the nature of this and the use of "where someid in (select someid from table where something) is probably not the best way to do it.
Question is, is there a better way?
I call these "set-within-a-set" queries, and like to approach them using group by and having:
select buyer_member_id
from purchase p
group by buyer_member_id
having sum(case when song_id = 1001 then 1 else 0 end) > 0 and
sum(case when song_id = 1003 then 1 else 0 end) > 0;
The sum() counts the number of purchases that match each song. The > 0 says there is at least 1. And = 0 would say there are none.
I have 4 tables in my database.
Students (Idno, Name, CourseId)
Sample data:
Idno Name CourseId
------------------------
-101123456 Vijay 101
-101123457 John 102
-101123458 Sam 101
-101123459 Arvind 102
-101123460 Smith 101
Courses (CourseId, CourseNo, CourseName, StreamId)
Sample data:
CourseId CourseNo CourseName StreamId
------------------------------------------
-101 53245 C 1
-102 53245 C++ 2
Streams (StreamId, StreamName)
Sample data:
StreamId StreamName
---------------------------
-1 Engineering
-2 Medical
Booking (BId, Idno, BStatus)
Sample data:
Bid Idno BStatus
--------------------------------
-1110 101123456 Confirmed
-1111 101123456 Confirmed
-1112 101123457 Confirmed
-1113 101123458 Confirmed
-1114 101123459 Confirmed
-1115 101123460 Confirmed
-1116 101123456 Confirmed
-1117 101123457 Confirmed
-1118 101123458 Confirmed
-1119 101123459 Confirmed
-1119 101123460 Cancelled
I have a problem generating the following output
SNo Stream BookedOnce BookedTwice NonBooked
1 Engineering 2 3 0
2 Medical 3 1 1
Thanks
I think this requires a two step process. First, calculate the number of bookings by each student for a stream. Then per stream, count the number of students that have one, two or zero bookings.
Here's an example, with the first step in the inner query:
select StreamId
, StreamName
, sum(case when Bookings = 1 then 1 else 0 end) as BookedOnce
, sum(case when Bookings = 2 then 1 else 0 end) as BookedTwice
, sum(case when Bookings = 0 then 1 else 0 end) as NoneBooked
from (
select str.StreamId
, str.StreamName
, s.Idno
, count(b.BId) as Bookings
from Students s
left join
Booking b
on b.Idno = s.Idno
left join
Courses c
on c.CourseId = s.CourseId
left join
Streams str
on str.StreamId = c.StreamId
group by
str.StreamId
, str.StreamName
, s.Idno
) BookingsPerStudentPerStream
group by
StreamId
, StreamName