Create Aggregate Table from binary columns on SQL - sql

Table1
customerid
jan21
feb21
mar21
apr21
123
1
0
0
1
124
0
1
0
1
125
0
0
1
1
126
1
1
0
1
i have a table as above and I would like to create another table like this:
Table2
month
jan21
feb21
mar21
apr21
jan21
2
0
0
2
feb21
-
2
0
2
mar21
-
-
1
2
apr21
-
-
-
4
Basically, I would like to count how many customers are active in each month on condition that they are active in the given month.
I left empty for the left side of the table since it will be the symmetrical of the right side but if someone may come with a solution filled both sides of the table, it is still more than welcome.
I do it with lots of subqueries and WHERE conditions and I sum each month by changing the where condition for each month every time. However, it is very time consuming and I wonder if there is a one time SQL query to do this task.
By the way, I am working on POSTGRESQL.
Here is my query. I also create table1 within the query as well.
I change the jan21 = 1 for each month
select
sum(jan21) as jan21,
sum(feb21) as feb21,
sum(mar21) as mar21,
sum(apr21) as apr21
from(
select customerid,
sum(jan21) as jan21,
sum(feb21) as feb21,
sum(mar21) as mar21,
sum(apr21) as apr21
from (
with orders as (
select customerid, to_char(orderdate, 'YYYYMM') as orderdate
from ordertable
group by customerid, orderdate
)
select distinct customerid,
case
when orderdate = '202101' then 1 else 0 end as jan21,
case
when orderdate = '202102' then 1 else 0 end as feb21,
case
when orderdate = '202103' then 1 else 0 end as mar21,
case
when orderdate = '202104' then 1 else 0 end as apr21
from orders
) t1 group by customerid
) t25
where jan21 = 1

Related

How to merge two query results joining same date

let's say there's a table have data like below
id
status
date
1
4
2022-05
2
3
2022-06
I want find count of id of each month by their status. Something like this below
date
count(status1) = 4
count(status2) =3
2022-05
1
null
2022-06
null
1
I tried doing
-- select distinct (not working)
select date, status1, status2 from
(select date, count(id) as "status1" from myTable
where status = 4 group by date) as myTable1
join
(select date, count(id) as "status2" from myTable
where status = 3 group by date) as myTable2
on myTable1.date = myTable2.date;
-- group by (not working)
but it does duplicate the data needed.
and I am using SQL Server.
select d.date,
sum
(
case
when d.status=4 then 1
else 0
end
)count_status_4,
sum
(
case
when d.status=5 then 1
else 0
end
)count_status_5
from your_table as d
group by d.date

Group by to include case statement inside SQL statement

I have a table which stores purchase info from sellers and table contains rating to every purchase out of 5 stars. I want to have output Group By sellers and Each sellers good(Above 3) and bad(Below 4) ratings count
PurchaseId SellerId PurchaseRating
1 ABC 2
2 ABC 5
3 DEF 1
4 XYZ 2
5 DEF 4
7 ABC 3
OUTPUT
SellerId TotalOrders AvgRating Above3*(4&5) Below4*(1 to 3)
ABC 3 3.3 1 2
DEF 2 2.5 1 1
XYZ 1 2 0 1
For first 3 columns I am getting result using this
Select SellerId, Count(P.Id) TotalOrders, Avg(PurchaseRating) AvgRating,
CASE WHEN P.PurchaseRating >= 4 THEN 1 ELSE 0 END AS Above3*(4&5)
from
[dbo].[AmazonAbeOrdersPurchaseInfo] P
Where PurchaseRating is not null
Group by P.SellerId
order by TotalOrders desc
Unable to identify how to include case in group by clause.
You are trying to do conditional aggreation. The important thing is that you want the conditional expression inside the aggregate function:
select
sellerId,
count(*) totalOrders,
avg(purchaseRating) avgRating,
sum(case when purchaseRating > 3 then 1 else 0 end) above3,
sum(case when purchaseRating < 4 then 1 else 0 end) below4
from [dbo].[AmazonAbeOrdersPurchaseInfo]
where purchaseRating is not null
group by sellerId
order by totalOrders desc

Checking if the row has the max value in a group

I'm trying get to find out if a row has the max value in a group. Here's really simple example:
Data
VoteCount LocationId UserId
3 1 1
4 1 2
3 2 2
4 2 1
Pseudo-query
select
LocationId,
sum(case
when UserId = 1 /* and has max vote count*/
then 1 else 0
end) as IsUser1Winner,
sum(case
when UserId = 2 /* and has max vote count*/
then 1 else 0
end) as IsUser2Winner
from LocationVote
group by LocationID
It should return:
LocationId IsUser1Winner IsUser2Winner
1 0 1
2 1 1
I also couldn't find a way to generate dynamic column names here. What would be the simplest way to write this query?
You could also do this using a Case statement
WITH CTE as
(SELECT
MAX(VoteCount) max_votes
, LocationId
FROM LocationResult
group by LocationId
)
SELECT
A.LocationId
, Case When UserId=1
THEN 1
ELSE 0
END IsUser1Winner
, Case when UserId=2
THEn 1
ELSE 0
END IsUser2Winner
from LocationResult A
inner join
CTE B
on A.VoteCount = B.max_votes
and A.LocationId = B.LocationId
Try this:
select *
from table t
cross apply (
select max(votes) max_value
from table ref
where ref.group = t.group
)votes
where votes.max_value = t.votes
but if your table is huge and has no propriate indexes performance may be poor
Another way is to get max values by groups into table variable or temp table and then join it to original table.

how to substract two sums based on different field values in one table (view)

example: i need the numbers of which the sum of the amount where id = 1 is not equal of the sum of the amount where id <> 1, together with the id's and difference.
The table (view in this case) may look like this:
NUMBER AMOUNT ID
0001 500 1
0001 500 2
0002 400 3
0003 299 1
0003 300 3
0003 300 3
Many thanks for your help on this one.
Using this query you can get sum of amounts grouped by number, with ID equals 1 and ID not equals 1.
SELECT NUMBER
, SUM(CASE WHEN ID = 1 THEN AMOUNT ELSE 0 END) AS Equals1
, SUM(CASE WHEN ID <> 1 THEN AMOUNT ELSE 0 END) AS NotEquals1
FROM DataTable
GROUP BY NUMBER
If this is you expected, use following query to get difference amounts grouped by number.
SELECT NUMBER, (Equals1 - NotEquals1) AS DifferenceAmount
FROM
(
SELECT NUMBER
, SUM(CASE WHEN ID = 1 THEN AMOUNT ELSE 0 END) AS Equals1
, SUM(CASE WHEN ID <> 1 THEN AMOUNT ELSE 0 END) AS NotEquals1
FROM DataTable
GROUP BY NUMBER
) AS GroupedTable
WHERE Equals1 <> NotEquals1
SELECT
MyView.Number,
MyView.Amount - D.OverOneTotal AS Difference
FROM
MyView
LEFT JOIN
(SELECT
Number,
SUM(Amount) OverOneTotal
FROM
MyView
WHERE
ID > 1
GROUP BY
Number) D ON MyView.Number = D.Number
WHERE
MyView.ID = 1
AND MyView.Amount <> D.OverOneTotal

How to write SQL query to select percentage with inner join?

I have two table like
First table
FtID ItemName Days
---------------------------------------
1 Drive 10
2 Run 5
3 Read 21
Second Table
StID FtID Completed (bit) DateTime
-----------------------------------------------------
1 1 0 07/11/2011
2 1 1 08/11/2011
3 1 1 09/11/2011
4 1 0 10/11/2011
5
I need to select from both tables and get data like you can see below
ItemName Days DaysCompleted DaysIncompleted PercentageCompletion DaysLeft
---------------------------------------------------------------------------------------
Drive 10 2 2 40% 6
SELECT
t1.ItemName,
t1.Days,
SUM(Cast(t2.Completed as integer)) DaysCompleted ,
COUNT(t2.StID) - SUM(Cast(t2.Completed as integer)) DaysIncompleted ,
(COUNT(t2.StID) * 100 / t1.Days)) PercentageCompletion ,
t1.Days - SUM(CASE WHEN DateTime > GetDate() THEN 1 ELSE 0)) DaysLeft
FROM
table1 t1
INNER JOIN table2 t2
ON t1.ftId = t2.ftID
GROUP BY
t1.ItemName,
t1.Days
As JNK noted if Completed is Indeed a BIT you'll need to cast to an integer (or use SUM/CASE as with his answer). Also I outputted Percentage Complete as 40 if you want .40 then you'll need to cast to decimal if t1.Days is not already a decimal.
SELECT
ItemName,
Days,
sum(cast(Completed as integer) as DaysCompleted,
(max(DateTime) - min(DateTime)) - sum(cast(Completed as integer) as Days Incompleted,
(Days - (max(DateTime) - min(DateTime))) / Days as Percentage Completion,
Days - (max(DateTime) - min(DateTime)) as DaysLeft
FROM
Table_1 T1 INNER JOIN Table_2 T2 ON T1.FtID = T2.FtID
GROUP BY
ItemName,
Days
Start with this / play around with it
try:
SELECT f.ItemName,
f.Days,
SUM(CASE WHEN s.Completed = 1 THEN 1 ELSE 0 END) as DaysCompleted,
SUM(CASE WHEN s.completed = 0 THEN 1 ELSE 0 END) as DaysIncompleted,
CAST((COUNT(s.completed)/f.days) as DECIMAL(10,8)) as PercentageComplete,
(f.days-(COUNT(s.completed)) as DaysLeft
FROM FirstTable F
INNER JOIN SecondTable S
ON F.FTID = S.FTID
GROUP BY f.itemname, f.days