SQL "Group" and "Count" categories - sql-server-2012

Edit. This is a follow up from another question. To simplify the question. Assume a table
date | id | type
01/01 | 1 | F
02/01 | 1 | F
02/01 | 1 | F
03/01 | 1 | S
03/01 | 1 | S
04/01 | 1 | F
04/01 | 1 | S
05/01 | 1 | S
I am looking for a way to summarise the above table by combination of transaction types per day. If a person (id) has only one transaction per day it counts as a Single type. If they have more than one it counts as a Multiple one. I've done that with my original query and it works. The output from the above table would be:
date | Single | Multiple
01/01 | 1 | 0
02/01 | 0 | 1
03/01 | 0 | 1
04/01 | 0 | 1
05/01 | 1 | 0
I got that far and it works. What's I'm struggling with (ie. don't have a clue of how to start) is how set up a query to show all possible combinations of Type (SS, FF, FS) instead of just counting the multiple transactions. The desired output would be like:
date | Single | # FF | # FS | # SS
01/01 | 1 | 0 | 0 | 0
02/01 | 0 | 1 | 0 | 0
03/01 | 0 | 0 | 0 | 1
04/01 | 0 | 0 | 1 | 0
05/01 | 1 | 0 | 0 | 0
Any constructive hints or ideas will be much appreciated.

this is assuming that you have max 2 types per date.
You can use the CASE WHEN statement with MIN() and MAX() to check for combination of FF, FS or SS
select [date],
case when count(*) = 1 then 1 else 0 end as Single,
case when count(*) >= 2
and min([type]) = 'F'
and max([type]) = 'F'
then 1
else 0
end as [# FF],
case when count(*) >= 2
and min([type]) = 'F'
and max([type]) = 'S'
then 1
else 0
end as [# FS],
case when count(*) >= 2
and min([type]) = 'S'
and max([type]) = 'S'
then 1
else 0
end as [# SS]
from yourtable
group by [date]
EDIT :
for more then 3 types, just change the count(*) = 2 to count(*) >= 2 as long as the type are either F or S

Related

How to get count based on Values in SQL

I've something to execute I need the count based on values. Here is my Table
"ORD_NUM","ORD_AMOUNT","ORD_DATE","CUST_CODE","AGENT_CODE","ORD_DESCRIPTION"
"200118"|"500"|"07/20/2008"|"C00023"|"A006"|"SOD"
"200120"|"500"|"07/20/2008"|"C00009"|"A002"|"SOD"
"200129"|"1000"|"07/20/2008"|"C00024"|"A006"|"SOD"
"200127"|"1000"|"08/20/2008"|"C00015"|"A003"|"SOD"
"200128"|"500"|"08/20/2008"|"C00009"|"A002"|"SOD"
"200128"|"500"|"09/20/2008"|"C00009"|"A002"|"SOD"
"200128"|"1000"|"09/20/2008"|"C00009"|"A002"|"SOD"
"200128"|"1000"|"10/20/2008"|"C00009"|"A002"|"SOD"
In the order amount we only have either 1000 or 500. We need to return the data count of 500 and 1000 for each date. EX:
Date |1000Count|500Count
07/20/2008| 1 | 2
08/20/2008| 1 | 1
09/20/2008| 1 | 1
10/20/2008| 1 | 1
Thanks! to SlavaRozhnev
select
d,
count(case when amount = 500 then 1 end) count500,
count(case when amount = 1000 then 1 end) count1000
from test
group by d;
Gives Result :
+============+==========+===========+
| d | count500 | count1000 |
+============+==========+===========+
| 2023-01-01 | 4 | 2 |
| 2023-01-02 | 1 | 1 |
| 2023-01-03 | 0 | 1 |
| 2023-01-04 | 1 | 0 |
+------------+----------+-----------+

Get number of times a user has availed a particular offer

I have a table which gives information about when a particular user has used an offer. It has 3 columns
Date: Date at which the offer was used
user_id: Identifier for a particular user
txn_id: Transaction id when a user uses an offer. It is always unique in the table.
The offer is such that a particular user can use it for 5 times.
I want to know at each date the number of users are in which stage of offer usage.
For example
On Day 1 there could be 3 users who have used offer once(redemption_1), 2 users who could have used offer twice (redemption_2).
Now on Day 2 there could be users from day 1(repeat users) as well as users who are coming for offer usage for the first time(new users).
For the new users of day 2 the logic is same as that of day 1 users.(May be 2 new users use the offer for 1 time(redemption_1), 3 new users use it for 3 times(redemption_3))
But for the repeat users now I want to add up to there previous day's usage.
For example
On Day 1, 3 users had used offer once(redemption_1) but on day 2 if they use it one more time then they should be counted in redemption_2.(And not in redemption_1 since they are using it for second time since the offer has started/or there last usage)
In this way I want to go on adding cumulatively the number of time a user has used a offer and the count the number of users who have used offer for 1 time(redemption_1), 2 time(redemption_2) and so on for each date
Table
+------------+---------+------------+
| Date | user_id | txn_id |
+------------+---------+------------+
| 2019-06-04 | 1 | 1ACSA0-ABA |
| 2019-06-04 | 2 | 1BEAA0-CSC |
| 2019-06-04 | 3 | 1AGHF0-CBA |
| 2019-06-04 | 1 | 1AVFA0-GAA |
| 2019-06-05 | 1 | 1BCFA0-AAA |
| 2019-06-05 | 1 | 1AVFB0-GAC |
| 2019-06-05 | 2 | 1AVFA0-GVA |
| 2019-06-05 | 4 | 1AVFA0-GVB |
| 2019-06-05 | 5 | 1AVFA0-BCF |
| 2019-06-06 | 6 | 1AGHF0-CCA |
| 2019-06-06 | 1 | 1BXHF0-CCA |
| 2019-06-06 | 2 | 1AGHF0-CBG |
| 2019-06-06 | 3 | 1AGHF0-CAW |
| 2019-06-06 | 2 | 1AGHF0-CTU |
+------------+---------+------------+
Desired Output
+------------+--------------+--------------+--------------+--------------+--------------+
| Date | redemption_1 | redemption_2 | redemption_3 | redemption_4 | redemption_5 |
+------------+--------------+--------------+--------------+--------------+--------------+
| 2019-06-04 | 2 | 1 | 0 | 0 | 0 |
| 2019-06-05 | 2 | 1 | 0 | 1 | 0 |
| 2019-06-06 | 1 | 1 | 0 | 1 | 1 |
+------------+--------------+--------------+--------------+--------------+--------------+
I will walk you through the rows of output for better understanding
In row one with date 2019-06-04 there are two users who used offer once (2,3) and one user who used offer twice(1)
In row with date 2019-06-05 there are 2 user who used offer once(4,5). Note that they have never used offer before that so they counted for redemption_1.
In the same row there is 1 user who has used offer 2 times (2: Once on 2019-06-04 and then on 2019-06-05) so he is counted for redemption_2
In the same row there is 1 user who has used offer 4 times (1: Twice on 2019-06-04 and then again twice on 2019-06-05) so he is counted for redemption_4
And so on for row with date 2019-06-06
Please let me know for any kind of clarification
Not a paragon of efficiency, but it works.
Test data:
Create Table offer_used(date DateTime, user_id Int, txn_id Varchar(50))
Insert Into dbo.offer_used (date,
user_id,
txn_id)
Values
('2019-06-04', 1, '1ACSA0-ABA'),
('2019-06-04', 2, '1BEAA0-CSC'),
('2019-06-04', 3, '1AGHF0-CBA'),
('2019-06-04', 1, '1AVFA0-GAA'),
('2019-06-05', 1, '1BCFA0-AAA'),
('2019-06-05', 1, '1AVFB0-GAC'),
('2019-06-05', 2, '1AVFA0-GVA'),
('2019-06-05', 4, '1AVFA0-GVB'),
('2019-06-05', 5, '1AVFA0-BCF'),
('2019-06-06', 6, '1AGHF0-CCA'),
('2019-06-06', 1, '1BXHF0-CCA'),
('2019-06-06', 2, '1AGHF0-CBG'),
('2019-06-06', 3, '1AGHF0-CAW'),
('2019-06-06', 2, '1AGHF0-CTU')
Query:
; With
Dates As (Select Distinct date From dbo.offer_used OU),
Users As (Select user_id, FirstTime = Min(date) From dbo.offer_used OU Group By user_id),
UserCounts As (Select
Dates.date,
Users.user_id,
Users.FirstTime,
UsedCount = (Select Count(*) From dbo.offer_used As Used
Where Used.date <= Dates.date
And Used.user_id = Users.user_id)
From
Dates
Cross Join Users)
Select
date = UserCounts.date,
[first time today] = Sum(Case When UserCounts.date = UserCounts.FirstTime
And UserCounts.UsedCount = 1 Then 1 Else 0 End),
[2 times total] = Sum(Case When UserCounts.UsedCount = 2 Then 1 Else 0 End),
[3 times total] = Sum(Case When UserCounts.UsedCount = 3 Then 1 Else 0 End),
[4 times total] = Sum(Case When UserCounts.UsedCount = 4 Then 1 Else 0 End),
[5 times total] = Sum(Case When UserCounts.UsedCount = 5 Then 1 Else 0 End),
[bonus: never] = Sum(Case When UserCounts.UsedCount = 0 Then 1 Else 0 End)
From UserCounts
Group By UserCounts.date
Order By UserCounts.date
Results:
date first time today 2 times total 3 times total 4 times total 5 times total bonus: never
----------- ---------------- ------------- ------------- ------------- ------------- ------------
2019-06-04 2 1 0 0 0 3
2019-06-05 2 1 0 1 0 1
2019-06-06 1 1 0 1 1 0
I think you want conditional aggregation:
select t.date,
sum(case when seqnum = 1 then 1 else 0 end) as redemption_1,
sum(case when seqnum = 2 then 1 else 0 end) as redemption_2,
sum(case when seqnum = 3 then 1 else 0 end) as redemption_3,
sum(case when seqnum = 4 then 1 else 0 end) as redemption_4,
sum(case when seqnum = 5 then 1 else 0 end) as redemption_5
from (select t.*, row_number() over (partition by user_id order by date) as seqnum
from table t
) t
group by t.date
order by t.date

SQL sum total each column in last row

I wish SQL for SUM each column(IPO and UOR) in TOTAL in second last. And GRAND TOTAL(Sum IPO + UOR) in the last one. Thank you so much
No Code IPO UOR
----------------------
1 D173 1 0
2 D176 3 0
3 D184 1 1
4 D185B 1 0
5 D187 1 2
6 F042 3 0
7 ML004 12 3
8 TTPMC 2 0
9 Z00204 1 0
------------------
TOTAL (NOS) 25 6
-------------------------
GRAND TOTAL (NOS) 31
Here is my code, :
SELECT
SUM(CASE WHEN IPOType = 'IPO' THEN 1 ELSE 0 END) as IPO,
SUM(CASE WHEN IPOType = 'UOR' THEN 1 ELSE 0 END) as UOR
FROM IPO2018
GROUP BY OriProjNo
it can show like this
No Code IPO UOR
----------------------
1 D173 1 0
2 D176 3 0
3 D184 1 1
4 D185B 1 0
5 D187 1 2
6 F042 3 0
7 ML004 12 3
8 TTPMC 2 0
9 Z00204 1 0
------------------
Generally speaking, you want to leave totals and sub-totals to whatever tool you are presenting your data in, as they will be able to handle the formatting with significantly more ease. In addition, your desired output does not have the same number of columns (Grand Total row only has one numeric) so even if you did shoehorn this in to the same dataset, the column headings wouldn't make sense.
That said, you can return group totals via the with rollup statement. This will provide an additional row with the aggregate totals for the group. Where there is more than one group in your data, you will get a sub-total row for each group and a total row for the entire dataset:
declare #t table(c nvarchar(10),t nvarchar(3));
insert into #t values ('D173','IPO'),('D176','IPO'),('D176','IPO'),('D176','IPO'),('D184','IPO'),('D184','UOR'),('D185B','IPO'),('D187','IPO'),('D187','UOR'),('D187','UOR'),('F042','IPO'),('F042','IPO'),('F042','IPO'),('TTPMC','IPO'),('TTPMC','IPO'),('Z00204','IPO'),('ML004','UOR'),('ML004','UOR'),('ML004','UOR'),('ML004','IPO'),('ML004','IPO'),('ML004','IPO'),('ML004','IPO'),('ML004','IPO'),('ML004','IPO'),('ML004','IPO'),('ML004','IPO'),('ML004','IPO'),('ML004','IPO'),('ML004','IPO'),('ML004','IPO');
select row_number() over (order by grouping(c),c) as n
,case when grouping(c) = 1 then 'TOTAL (NOS)' else c end as c
,sum(case when t = 'IPO' then 1 else 0 end) as IPO
,sum(case when t = 'UOR' then 1 else 0 end) as UOR
from #t
group by c
with rollup
order by grouping(c)
,c;
Output:
+----+-------------+-----+-----+
| n | c | IPO | UOR |
+----+-------------+-----+-----+
| 1 | D173 | 1 | 0 |
| 2 | D176 | 3 | 0 |
| 3 | D184 | 1 | 1 |
| 4 | D185B | 1 | 0 |
| 5 | D187 | 1 | 2 |
| 6 | F042 | 3 | 0 |
| 7 | ML004 | 12 | 3 |
| 8 | TTPMC | 2 | 0 |
| 9 | Z00204 | 1 | 0 |
| 10 | TOTAL (NOS) | 25 | 6 |
+----+-------------+-----+-----+

SQL charge reversal data

I'm extracting financial information, but ran into charge reversal information. Basically if someone was charged for a service there would be a column with that charge. If the charge was later reversed there would be another row with the exact same data, but with a charge reversal flag on it. I want to only get charges that are were not reversed at all.
Below is an example of what i mean and need. As you can see the RVSLInd column has a 1 if the charge is a reversal. The 0 represents an initial charge
I couldn't do: select * from from table where rvslInd = 0. because this would get rid of the reversal row only.
RvslInd|ExtPriceAmt
-------| ----------|
0 | 155.70 |
0 | 1.50 |
0 | 239.00 |
0 | 1111.00 |
1 | -1111.00 |
0 | 217.00 |
0 | 1491.00 |
1 | -1491.00 |
0 | 388.00 |
0 | 72.00 |
This is what I want to be able to get back:
RvslInd|ExtPriceAmt
-------| ----------|
0 | 155.70 |
0 | 1.50 |
0 | 239.00 |
0 | 217.00 |
0 | 388.00 |
0 | 72.00 |
this would be my new table with a customer column added:
CustomerID|RvslInd|ExtPriceAmt
----------|-------| ----------|
1 | 0 | 155.70 |
1 | 0 | 1.50 |
1 | 0 | 239.00 |
2 | 0 | 217.00 |
2 | 0 | 388.00 |
2 | 0 | 72.00 |
Given your data, you cannot reliably do what you want. For the data you have shown, you could do:
select ExtPriceAmt
from t
where RvslInd = 0 and
not exists (select 1 from t t2 where t2.ExtPriceAmt = - t.ExtPriceAmt and t2.RvslInd = 1);
The problem is when the price is repeated. That gets in the way.
That said, all is not hopeless. You can get a list of the prices along with the number of non-reversed times:
select ExtPriceAmt,
sum(case when RvslInd = 0 then 1 when RvslInd = 1 then -1 end) as non_reversed_count
from t
group by ExtPriceAmt
having sum(case when RvslInd = 0 then 1 when RvslInd = 1 then -1 end) > 0;

SQL Server : get Count() of a related table column where some condition

Given tables CollegeMajors
| Id | Major |
|----|-------------|
| 1 | Accounting |
| 2 | Math |
| 3 | Engineering |
and EnrolledStudents
| Id | CollegeMajorId | Name | HasGraduated |
|----|----------------|-----------------|--------------|
| 1 | 1 | Grace Smith | 1 |
| 2 | 1 | Tony Fabio | 0 |
| 3 | 1 | Michael Ross | 1 |
| 4 | 3 | Fletcher Thomas | 1 |
| 5 | 2 | Dwayne Johnson | 0 |
I want to do a query like
Select
CollegeMajors.Major,
Count(select number of students who have graduated) AS TotalGraduated,
Count(select number of students who have not graduated) AS TotalNotGraduated
From
CollegeMajors
Inner Join
EnrolledStudents On EnrolledStudents.CollegeMajorId = CollegeMajors.Id
and I'm expecting these kind of results
| Major | TotalGraduated | TotalNotGraduated |
|-------------|----------------|-------------------|
| Accounting | 2 | 1 |
| Math | 0 | 1 |
| Engineering | 1 | 0 |
So the question is, what kind of query goes inside the COUNT to achieve the above?
Select CollegeMajors.Major
, COUNT(CASE WHEN EnrolledStudents.HasGraduated= 0 then 1 ELSE NULL END) as "TotalNotGraduated",
COUNT(CASE WHEN EnrolledStudents.HasGraduated = 1 then 1 ELSE NULL END) as "TotalGraduated"
From CollegeMajors
InnerJoin EnrolledStudents On EnrolledStudents.CollegeMajorId = CollegeMajors.Id
GROUP BY CollegeMajors.Major
You can use the CASE statement inside your COUNT to achieve the desired result.Please try the below updated query.
Select CollegeMajors.Major
, COUNT(CASE WHEN EnrolledStudents.HasGraduated= 0 then 1 ELSE NULL END) as "TotalNotGraduated",
COUNT(CASE WHEN EnrolledStudents.HasGraduated = 1 then 1 ELSE NULL END) as "TotalGraduated"
From CollegeMajors
InnerJoin EnrolledStudents On EnrolledStudents.CollegeMajorId = CollegeMajors.Id
GROUP BY CollegeMajors.Major
You can try this for graduated count:
Select Count(*) From EnrolledStudents group by CollegeMajorId having HasGraduated = 1
And change 1 to zero for not graduated ones:
Select Count(*) From EnrolledStudents group by CollegeMajorId having HasGraduated = 0