Organizing data into multiple columns SQL - sql

I have a table of more than 5m customers with historic activity data as in example below:
Customer ID
PART_ID
Activity
12345
202012
2
12345
202101
0
12345
202102
5
I want to convert this data into multiple columns; customers at the rows, dates in the columns and their respective activity info.
I wrote the code below but instead of creating a single row for a single customer, customers duplicate and I get a table like this:
Customer ID
202012
202101
202102
12345
1
0
0
12345
0
0
0
12345
0
0
1
Instead of:
Customer ID
202012
202101
202102
12345
1
0
1
What am I doing wrong?
SELECT *
FROM
(
SELECT CUST_ID, RULLED_PROFIT_CENTER,
CASE WHEN PART_ID = 202012 AND ACTIVITY > 0 THEN 1 ELSE 0 END AS ARA_20,
CASE WHEN PART_ID = 202101 AND ACTIVITY > 0 THEN 1 ELSE 0 END AS OCA_21,
CASE WHEN PART_ID = 202102 AND ACTIVITY > 0 THEN 1 ELSE 0 END AS SUB_21,
CASE WHEN PART_ID = 202103 AND ACTIVITY > 0 THEN 1 ELSE 0 END AS MAR_21,
CASE WHEN PART_ID = 202104 AND ACTIVITY > 0 THEN 1 ELSE 0 END AS NIS_21,
CASE WHEN PART_ID = 202105 AND ACTIVITY > 0 THEN 1 ELSE 0 END AS MAY_21,
CASE WHEN PART_ID = 202106 AND ACTIVITY > 0 THEN 1 ELSE 0 END AS HAZ_21,
CASE WHEN PART_ID = 202107 AND ACTIVITY > 0 THEN 1 ELSE 0 END AS TEM_21,
CASE WHEN PART_ID = 202108 AND ACTIVITY > 0 THEN 1 ELSE 0 END AS AGU_21,
CASE WHEN PART_ID = 202109 AND ACTIVITY > 0 THEN 1 ELSE 0 END AS EYL_21
FROM ACTIVITY
WHERE RULLED_PROFIT_CENTER IN (108, 103, 170)
GROUP BY CUST_ID, RULLED_PROFIT_CENTER
)
WHERE ARA_20 + OCA_21 + SUB_21 + MAR_21 + NIS_21 + MAY_21 +
HAZ_21 + TEM_21 + AGU_21 + EYL_21 > 0

WHERE RULLED_PROFIT_CENTER IN (108, 103, 170)
GROUP BY CUST_ID, RULLED_PROFIT_CENTER
You're grouping by two values, the customer and RULLED_PROFIT_CENTER. RULLED_PROFIT_CENTER can have three possible values, so you're going to get up to three rows per customer. If you want only one row per customer, remove RULLED_PROFIT_CENTER from your group by.
It's possible you added RULLED_PROFIT_CENTER to the group by because the query didn't work without it. If you want to include RULLED_PROFIT_CENTER in your select, but not your group by, you need to aggregate RULLED_PROFIT_CENTER as well. Even if each customer only has one value for it. Use string_agg.
SELECT CUST_ID, string_agg(RULLED_PROFIT_CENTER, ', ')
If a customer has only one value for RULLED_PROFIT_CENTER you'll get just that one value.
Since you're grouping all the rows of a customer together, you will need to take the max of your activities.
MAX( CASE WHEN PART_ID = 202012 AND ACTIVITY > 0 THEN 1 ELSE 0 END ) AS ARA_20

Related

SQL: SUM OR COUNT with CASE WHEN condition in multiple criteria

Course name
Section number
Course type
MATH 101
1
In person
MATH 101
2
In person
MATH 101
3
Online
MATH 101
4
In person
SOC 101
1
In person
SOC 101
2
In person
SOC 101
3
In person
ENGL 201
1
In person
ENGL 201
2
Online
ENGL 201
3
Online
ENGL 201
4
In person
PHY 101
1
Online
PHY 101
2
Online
From this table, I'd like to count Courses with only an 'In person' course, an 'Online' course, and both course types.
The query I tried is below.
SELECT
SUM(CASE WHEN coursetype = 'Inperson' AND coursetype = 'Online' THEN 1 ELSE 0 END) AS bothtype,
SUM(CASE WHEN coursetype = 'Online' THEN 1 ELSE 0 END) AS Onlineonly,
SUM(CASE WHEN coursetype = 'Inperson' THEN 1 ELSE 0 END) AS Onlineonly
From Course
The result what I expected is
bothtpye
Onlineonly
Inpersononly
2
1
1
but I got
bothtpye
Onlineonly
Inpersononly
0
7
6
Please advise me to get through this.
Thank you.
My solution uses double conditional aggregation.
SELECT SUM (CASE WHEN In_Person > 0 AND Online > 0 THEN 1 ELSE 0 END) as bothtype,
SUM (CASE WHEN In_Person > 0 AND Online = 0 THEN 1 ELSE 0 END) as inpersononly,
SUM (CASE WHEN In_Person = 0 AND Online > 0 THEN 1 ELSE 0 END) as onlineonly
FROM (
SELECT Course_name,
SUM(CASE WHEN Course_type='In Person' THEN 1 ELSE 0 END) as In_Person,
SUM(CASE WHEN Course_type='Online' THEN 1 ELSE 0 END) as Online
FROM Course
GROUP BY Course_name
) tot
DEMO Fiddle
SUGGESTION ( using PL/SQL ! ) :
CREATE PROCEDURE countCourses(OUT bothtype INT,OUT Inpersononly INT,OUT Onlineonly INT)
begin
SELECT COUNT(*) INTO bothtype FROM Course;
select COUNT(*) INTO Inpersononly FROM Course
WHERE courseType = "In person";
select COUNT(*) INTO Onlineonly FROM Course
WHERE courseType = "Online";
end;
call countCourses(#bothtype,#Inpersononly,#Onlineonly);
SELECT #bothtype,#Inpersononly,#Onlineonly;
EXPLICATION :
Creating procedure to store the count of each type of course in OUT variable
Call the procedure with convenient parameters
Select out given parameters

create total accordif if column is filled

I want to count the number of total people who will attend to an event and this number is calculated by the person who invites and how many people will be invited.
So I have this info,
refInqCredLink RegisterDate Name Phone Email Horario Insc1 Insc2 Insc3
496 2019-08-29 15:38:13.183 Abilio 91 abilio#hotmail.com 3 albano jorge
497 2019-08-30 14:12:46.873 Duarte 25 duarte#sapo.pt 3 antonio
499 2019-08-30 14:48:29.067 AGOSTINHO 92 agostinho#gmail.com 1 Jorge Antonio Manuel Fernando
In this case "John" will attend the event and will invite "albano" and "jorge". So the total number of people in this line is 3.
I need to check if Insc1, Insc2 and Insc3 column have value and if so (<>'') count as 1 person. I need to create a view based in this table with this sum.
If I try this code,
Select
SUM(case when not Name is null then 1 else 0 end
+
case when not Insc1 is null then 1 else 0 end
+
case when not Insc2 is null then 1 else 0 end
+
case when not Insc3 is null then 1 else 0 end)
from LACTINFO_InquiryCredential_Link_Seminario
this returns 12 instead of 8.
I have 3 Names + 5 Invitees = 8
Try the following
Select
SUM(case when Insc1 is null or Insc1 = '' then 0 else 1 end
+
case when Insc2 is null or Insc2 = '' then 0 else 1 end
+
case when Insc3 is null or Insc3 = '' then 0 else 1 end)
+
count(*)
from tab
To get the total, you can use count():
select count(*) + -- everyone is counted once
count(insc1) + count(insc2) + count(insc3) -- additional invitees
from t;
First, note that this is a poor data model. You should have a separate table with one row per person invited.
Second, the same person could be invited multiple times. This formulation does not take that into account. Using just first names makes it hard to deduplicate the data.

Multiple times Grouping with where in SQL Server

I have a source table like this
ProductName SaleReceipt SaleCode
--------------------------------
F-Apple 1001 1
F-Orange 1002 2
G-Rice 1003 3
G-Barile 1005 4
G-Oats 1006 1
V-Carrot 1007 4
V-Cabbage 1008 3
V-Potato 1009 1
V-Tomato 1010 1
Chocolate 1011 4
Cookies 1012 1
Cakes 1013 2
I need to create a report like this
30 Day delay 60 Day Delay 90 day delay 120 day delay
Fruits 1 1 0 0
Grains 1 0 1 1
Vegetables 2 0 1 1
Other category 1 1 0 1
The conditions to create the report are:
All ProductName start with F is grouped as fruits
All ProductName start with G is grouped as Grains
All ProductName start with V is grouped as Vegetables
Other product name go in the other category
30 day delay: count of (SaleReceipt) with a SaleCode=1
60 day delay: count of (SaleReceipt) with a SaleCode=2
90 day delay: count of (SaleReceipt) with a SaleCode=3
120 day delay: count of (SaleReceipt) with a SaleCode>=4
I could not find how to do grouping two times. I am using SQL Server 2014.
You can use case in a group by. Or use a lookup table:
select coalesce(c.name, 'Other category'),
sum(case when salescode = 1 then 1 else 0 end) as salecode_1,
sum(case when salescode = 2 then 1 else 0 end) as salecode_2,
sum(case when salescode = 3 then 1 else 0 end) as salecode_3,
sum(case when salescode = 4 then 1 else 0 end) as salecode_4
from t left join
(values ('F-%', 'Fruit'), ('G-%' , 'Grain'), ('V-%', 'Vegetable')
) c(pattern, name)
on t.productname like c.pattern
group by coalesce(c.name, 'Other category');

SQL Query. limit an update per rows if condition is X and Y for the same ID number

Have the following table tblTrans where
Trans_ID Trans Sequence Trans_PointsEarned Trans_PointsApplied
4452 1 1 1
4452 2 1 1
4452 3 0 1
4462 1 1 1
4462 2 1 1
4462 3 1 1
4462 4 1 1
4462 5 1 1
9101 1 0 1
9101 2 0 1
9101 3 0 1
9101 4 0 1
(useless table doesnt work)
I need to set the following on another field per every customer ID.
So Customer_OverallPoints
4452 = 2 (doesn't count 0's)
4462 = 4 (I want to cap the points to 4 based on the sequence and transID and customerID)
9101 = 0 (dont count 0's).
This needs to be applied to thousands of records based on customerID and TransID where Trans_Sequence is within the same Trans_ID and it only counts the first 4 rows that have the Trans_pointsEarned = 1.
I tried putting a psuedocode together but it just looked ridicilous and I can't even come up with the logic for this.
Thanks
Assuming that TransId is really the customer id, I think the basic logic is just an aggregation:
select t.TransId,
(case when sum(t.Trans_PointsEarned) > 4 then 4
else sum(t.Trans_PointsEarned)
end) as Customer_OverallPoints
from tblTrans t
group by t.TransId;
You can put this into an update statement as:
update customers c
set Customer_OverallPoints = (select (case when sum(t.Trans_PointsEarned) > 4 then 4
else sum(t.Trans_PointsEarned)
end)
from tblTrans t
where t.TransId = c.CustomerId
);

Grouping sub query in one row

ClientID Amount flag
MMC 600 1
MMC 700 1
FDN 800 1
FDN 350 2
FDN 700 1
Using sql server,Below query I am getting 2 rows fro FDN. I just would like to combine Client values in one row.
Output should be like
Client gtcount, totalAmountGreaterThan500 lscount,AmountLessThan500
MMC 2 1300 0 0
FDN 2 1500 1 350
SELECT
f.ClientID,f.flag,
case when flag = 1 then count(*) END as gtcount,
SUM(CASE WHEN flag = 1 THEN Amount END) AS totalAmountGreaterThan500,
case when flag = 2 then count(*) END as lscount,
SUM(CASE WHEN Flag = 2 THEN Amount END) AS AmountLessThan500,
from
( select ClientID, Amount,flag from #myTable)f
group by ClientID,f.flag
Try
SELECT ClientID,
SUM(CASE WHEN flag = 1 THEN 1 ELSE 0 END) AS gtcount,
SUM(CASE WHEN flag = 1 THEN Amount ELSE 0 END) AS totalAmountGreaterThan500,
SUM(CASE WHEN flag = 2 THEN 1 ELSE 0 END) AS lscount,
SUM(CASE WHEN Flag = 2 THEN Amount ELSE 0 END) AS AmountLessThan500
FROM Table1
GROUP BY ClientID
Output:
| CLIENTID | GTCOUNT | TOTALAMOUNTGREATERTHAN500 | LSCOUNT | AMOUNTLESSTHAN500 |
|----------|---------|---------------------------|---------|-------------------|
| FDN | 2 | 1500 | 1 | 350 |
| MMC | 2 | 1300 | 0 | 0 |
Here is SQLFiddle demo
Looks like your desired output is off -- there aren't any mmc records less than 500. You can accomplish this using sum with case for each of your fields, removing flag from the group by:
SELECT
ClientID,
SUM(CASE WHEN flag = 1 THEN 1 END) as gtcount,
SUM(CASE WHEN flag = 1 THEN Amount END) AS totalAmountGreaterThan500,
SUM(CASE WHEN flag = 2 THEN 1 END) as ltcount,
SUM(CASE WHEN Flag = 2 THEN Amount END) AS AmountLessThan500
from myTable
group by ClientID
SQL Fiddle Demo
On a different note, not sure why you need the Flag field. If it's just being used to denote less than records, just add the logic to the query:
SUM(CASE WHEN Amount <= 500 Then ...)