SQL - How to turn the rows of a record into columns? - sql

i need some help here!!
I have a "users" table within my platform. In this table I have information like:
id = is the user ID
created_at = is the date that the user created an agreement within the platform
agent = is responsible for serving the user
This information is in the following format:
id | created_at | deal_id | agent (columns of the table)
1 | 2020-08-01 | 1 | 123456
1 | 2020-09-01 | 2 | 123456
1 | 2020-09-10 | 3 | 345676
1 | 2020-10-29 | 4 | 456677
I would like to bring this data as follows:
id | created_at1 | created_at2 | created_at3 | created_at4 | agent1 | agent2 | agent3 | agent4
1 | 2020-08-01 | 2020-09-01 | 2020-09-10 | 2020-10-29 | 123456 | 123456 | 345676 | 456677
Is it possible?
I tried to do it with minimum and maximum, but it would only return me two situations.
Remembering that I gave an example of a user, I wanted it to return to all ID's.

You can use conditional aggregation as follows:
Select t.id,
Max(case when deal_id = 1 then created_at end) as created_at1,
Max(case when deal_id = 2 then created_at end) as created_at2,
Max(case when deal_id = 3 then created_at end) as created_at3,
Max(case when deal_id = 4 then created_at end) as created_at4,
Max(case when deal_id = 1 then agent end) as agent1,
Max(case when deal_id = 2 then agent end) as agent2,
Max(case when deal_id = 3 then agent end) as agent3,
Max(case when deal_id = 4 then agent end) as agent4
From your_table t
Group by id

Related

sql query rows in columns

i would like to display the following table view in lines.
date | id | value_1 | value_2 |
------------------------+-----+--------------------
2006-12-31 05:00:00+01 | 1 | 12 | 8 |
2006-12-31 05:00:00+01 | 2 | 9 | 4 |
2006-12-31 04:40:00+01 | 1 | 4 | 14 |
2006-12-31 04:40:00+01 | 2 | 11 | 1 |
2006-12-31 04:20:00+01 | 1 | 5 | 4 |
2006-12-31 04:20:00+01 | 2 | 21 | 3 |
The values ​​are to be added. The value of value_1 with ID=1 is to be added to the value of value_2 ID=2. And the value of value_2 with ID=1 is to be added to the value of value_1 ID=2.
I would like to receive the following view. Output should be sorted by date.
date | Numerator | Denominator |
------------------------+-----------+-------------+
2006-12-31 05:00:00+01 | 16 | 15 |
2006-12-31 04:40:00+01 | 5 | 23 |
2006-12-31 04:20:00+01 | 8 | 25 |
I created the following sql query, but the values ​​are not added together
SELECT date,
sum(case when id=1 then value_1 end) as Numerator,
sum(case when id=2 then value_2 end) as Denominator
from table
group by date
Order By date DESC;
The value of value_1 with ID=1 is to be added to the value of value_2 ID=2.
SELECT date,
sum(case when id = 1 then value_1
when id = 2 then value_2
end) as new_value
from table
group by date
Order By date DESC;
I'm not sure why your result set has two value columns when your description only explains one.
Based on your description -- BUT NOT THE DATA -- the second column appears to be:
SELECT date,
sum(case when id = 1 then value_1
when id = 2 then value_2
end) as numerator,
sum(case when id = 1 then value_2
when id = 2 then value_1
end) as denominator
from table
group by date
Order By date DESC;
Is this what you want?:
SELECT
date_column,
SUM((CASE WHEN id = 1 THEN value_1 ELSE 0 END) + (CASE WHEN id = 2 THEN value_2 ELSE 0 END)) AS Numerator,
SUM((CASE WHEN id = 1 THEN value_2 ELSE 0 END) + (CASE WHEN id = 2 THEN value_1 ELSE 0 END)) AS Denominator
FROM table
GROUP BY date_column
ORDER BY date_column DESC;
Output:
| date | Numerator | Denominator |
|-----------------------|-----------|-------------|
|2006-12-31 05:00:00+01 | 16 | 17 |
|2006-12-31 04:40:00+01 | 5 | 25 |
|2006-12-31 04:20:00+01 | 8 | 25 |
I do not understand how you calculate de Denominator.

How to get rid off of duplicate results from JOIN after using aggregation functions with different GROUP BY

I'm trying to use aggregation functions on a table to track the time progression of tasks assigned to users.
Part of the table looks something like this:
Table tasks:
TaskID | UserID | RegistrationDate | StartDate | EndDate | Status
--------------------------------------------------------------------------
tId1 | uId1 | dd/10/2000 | dd/10/2000 | dd/10/2000 | completed
tId2 | uId2 | dd/10/2000 | dd/10/2000 | dd/12/2000 | completed
tId3 | uId3 | dd/10/2000 | dd/12/2000 | dd/02/2001 | completed
tId4 | uId4 | dd/10/2000 | dd/11/2000 | dd/11/2000 | cancelled
tId5 | uId5 | dd/11/2000 | dd/11/2000 | dd/01/2001 | completed
tId6 | uId6 | dd/12/2000 | NULL | NULL | registered
tId7 | uId7 | dd/12/2000 | dd/12/2000 | NULL | in progress
tId8 | uId8 | dd/01/2001 | dd/01/2001 | dd/01/2001 | cancelled
tId9 | uId9 | dd/01/2001 | dd/01/2001 | dd/02/2001 | completed
tId10 | uId10 | dd/02/2001 | NULL | NULL | registered
tId11 | uId11 | dd/02/2001 | dd/02/2001 | dd/02/2001 | completed
tId12 | uId12 | dd/02/2001 | dd/02/2001 | dd/02/2001 | completed
......
My goal is to be able to use aggregators according to different criteria so I can get something along these lines:
Year | Month | NewR | Completed_A| Completed_B | Cancelled_A | Cancelled_B | OpenR |
---------------------------------------------------------------------------------------
2000 | 10 | 4 | 3 | 1 | 1 | 0 | 0 |
2000 | 11 | 1 | 1 | 0 | 0 | 1 | 0 |
2000 | 12 | 2 | 0 | 1 | 0 | 0 | 2 |
2001 | 1 | 2 | 1 | 1 | 1 | 1 | 0 |
2001 | 2 | 3 | 1 | 4 | 0 | 0 | 3 |
......
RegistrationDate, StartDate and EndDate are all timestamps, and I am trying to sort my results on a chronological monthly basis. What I am having problem with is that I am doing this with a GROUP BY statement, and while I do want the time progression to be based on the registration date, I dont necessarily want to group my outputs by it. For instance, in the table above NewR are all registrations that occur on the month corresponding to RegistrationDate, Completed_A counts tasks that were registered at RegistrationDate and completed anywhen, while Completed_B counts tasks that were registered anywhen but completed on EndDate, thus to get criteria A I need to GROUP BY RegistrationDate, and to get criteria B I need to GROUP BY EndDate.
I need to be able to do this in a single query, the way Im currently doing it is in a messy nested SELECT with a JOIN, but I am getting redundant columns that I would like to get rid off and have not been able to figure out how. My code looks something like this:
SELECT *
FROM
(SELECT
TO_CHAR(RegistrationDate, 'yyyy-mm') AS MonthYear
, DATE_PART('year',RegistrationDate) AS rYear
, DATE_PART('month',RegistrationDate) AS rMonth
, COUNT(*) AS NewR
, SUM(CASE WHEN Status = 'Completed' THEN 1 ELSE 0 END) AS Completed_A
, SUM(CASE WHEN Status IN ('Registered','In Progress')
THEN 1 ELSE 0 END) AS OpenR
FROM Tasks
GROUP BY MonthYear, rYear, rMonth) AS T1
LEFT JOIN
(SELECT
TO_CHAR(EndDate, 'yyyy-mm') AS MonthYear
, DATE_PART('year', EndDate) AS EndYear
, DATE_PART('month', EndDate) AS EndMonth
, SUM(CASE WHEN Status = 'Completed' THEN 1 ELSE 0 END) AS Completed_B
FROM Tasks
GROUP BY MonthYear, EndYear, EndMonth) AS T2
ON T1.MonthYear = T2.MonthYear
ORDER BY MonthYear
I added a column that has both the month and year because that's the only way I could figure out how to simplify the correct sorting on the final joined output, and I am also getting some cumulative sums so it really helps me to just SUM (stuff) OVER (ORDER BY MonthYear)
Is there better way overall to use aggregation on a single statement with different GROUP BYs?
How can I get rid off the duplicate month, year and 'yyyy-mm' columns? I dont really want to not use the SELECT * at the top because on my actual table I have a lot more columns that I cannot afford to select one by one, and I am also doing this for a few other criteria that are also time-related (so in reality my table has as many duplicate redundant month and year columns as however many criterias I am using). I have tried adding a DISTINCT but that didnt help.
Oh and Im currently using PostgreSQL but theres a chance that this will be migrated to SQL Server. (I am however trying to use as much standard SQL as possible and avoiding particular SQL Server functionalities).
I did notice there were similar questions but the answers did not really help me for this one.
To get the result, you need to use subquery and you can't use SELECT *.
The following query using UNION SELECT may be similar to what you said.
SELECT
Year
, Month
, SUM(CASE WHEN DTYPE='R' THEN 1 ELSE 0 END) AS NewR
, SUM(CASE WHEN DTYPE='R' AND Status = 'Completed'
THEN 1 ELSE 0 END) AS Completed_A
, SUM(CASE WHEN DTYPE='E' AND Status = 'Completed'
THEN 1 ELSE 0 END) AS Completed_B
, SUM(CASE WHEN DTYPE='R' AND Status IN ('Registered','In Progress')
THEN 1 ELSE 0 END) AS OpenR
FROM
(SELECT
'R' AS DTYPE
, DATE_PART('year',RegistrationDate) AS Year
, DATE_PART('month',RegistrationDate) AS Month
, Status
FROM Tasks
UNION ALL
SELECT
'E' AS DTYPE
, DATE_PART('year',EndDate)
, DATE_PART('month',EndDate)
, Status
FROM Tasks) T
WHERE Year IS NOT NULL
GROUP BY Year, Month
ORDER BY Year, Month;

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 Group by columns to the same row without join

I have grouped sales from a sales view with sales below using
Select id, name, Count(*) as [Sales], product, amount
from vwSales
Group by
id,name, product, amount
ID | Name | Sales | Product | Amount
1 | Bob | 4 | Fridge | 40
1 | Bob | 12 | Washer | 120
2 | Anne | 5 | Fridge | 50
2 | Anne | 4 | Washer | 40
Is it possible to group these in to one row without using a join? So table looks something like
ID | Name | Fridge Sales | fridge Amt | Washer sales | washer amt
1 | Bob | 4 | 40 | 12 | 120
2 | Anne | 5 | 50 | 4 | 40
You can do conditional aggregation :
select id, name,
sum(case when Product = 'Fridge' then 1 else 0 end) as [Fridge Sales],
sum(case when Product = 'Fridge' then Amount else 0 end) as [fridge Amt],
sum(case when Product = 'Washer' then 1 else 0 end) as [Washer Sales],
sum(case when Product = 'Washer' then Amount else 0 end) as [Washer Amt]
from vwSales
Group by id, name;

SQL query to count different records and print in 1 row

I have the following table and I am looking for a SQL query which counts the rewards for each name and also print the last date a specific reward type was earned.
This is my table:
NAME | REWARD | DATE
----------+-------------+------------
Chris | small | 18.05.2014
Chris | small | 27.08.2015
Chris | big | 01.07.2014
Tom | big | 10.10.2016
Tom | big | 30.11.2017
The result should look like this:
NAME | BIG_REWARDS | SMALL_REWARDS | LAST_BIG_REWARD | LAST_SMALL_REWARD
----------+-------------+---------------+-----------------+-------------------
Chris | 1 | 2 | 01.07.2014 | 27.08.2015
Tom | 2 | 0 | 30.11.2017 |
I am using Firebird
You could use conditional aggregation:
SELECT name,
SUM(CASE WHEN reward = 'big' THEN 1 ELSE 0 END) AS BIG_REWARDS,
SUM(CASE WHEN reward = 'small' THEN 1 ELSE 0 END) AS SMALL_REWARDS,
MAX(CASE WHEN reward = 'big' THEN "DATE" END) AS LAST_BIG_REWARD,
MAX(CASE WHEN reward = 'small' THEN "DATE" END) AS LAST_SMALL_REWARD
FROM tab
GROUP BY name;