SQL Server query + joining results - sql

I have a query like this:
SELECT recipientid AS ID,
COUNT(*) AS Recieved FROM Inbox
GROUP BY recipientid
UNION
SELECT SenderId,
COUNT(*) AS [Sent] FROM Inbox
GROUP BY SenderId
The output:
RecipientID Recieved
001 3
001 4
002 4
002 2
003 18
003 55
How can I rewrite is such a way that it displays like this:
RecipientID Recieved Sent
001 3 4
002 4 2
003 18 55
Thanks.

Just join the subqueries:
select a.ID,Received,Sent
from(
SELECT recipientid AS ID,
COUNT(*) AS Recieved FROM Inbox
GROUP BY recipientid
)a
full outer join(
SELECT SenderId as ID,
COUNT(*) AS [Sent] FROM Inbox
GROUP BY SenderId
)b
on (a.ID = b.ID)
order by a.ID;
Note that this grabs all of the sent and received values for any recipients or senders. If you only want results for IDs belonging to recipients and senders, then do an inner join.

I would add a source column to your query and do a simple pivot
select ID,
max (case when source=1 then Cnt else 0 end) as Received,
max (case when source=2 then Cnt else 0 end) as Sent
from (
SELECT 1 as Source,
recipientid AS ID,
COUNT(*) AS Cnt
FROM Inbox
GROUP BY recipientid
UNION
SELECT 2 as Source,
SenderId,
COUNT(*)
FROM Inbox
GROUP BY SenderId
) x
GROUP BY ID

If it's Postgres, MS SQL or others that support CTEs -
With Both as
(
SELECT
recipientid AS ID,
Count(*) AS Recieved,
0 as [Sent]
FROM Inbox
GROUP BY recipientid
UNION
SELECT
SenderId as ID,
0 as Recieved,
Count(*) AS [Sent]
FROM Inbox
GROUP BY SenderId
)
SELECT
ID,
Sum(Received) as [Received],
Sum(Sent) as [Sent]
FROM BOTH
GROUP BY ID
ORDER BY 1

Assuming you have a users table with the IDs, you could do something like:
SELECT
users.id,
COUNT(sent.senderid) AS sent,
COUNT(received.recipientid) AS received
FROM
users
LEFT JOIN inbox AS sent ON sent.senderid = users.id
LEFT JOIN inbox AS received ON received.recipientid = users.id
GROUP BY sent.senderid, received.recipientid
ORDER BY users.id;

Related

numbers of users buying the exact same product from the same shop for > 2 times in 1 years

I have data like this:
date user prod shop cat1 cat2
2022-02-01 1 a a ah g
2022-02-02 1 a1 b ah g
2022-04-03 1 a a ah g
2022-04-19 1 a a ah g
2022-05-01 2 b c bg g
I want to know how many user buy the same product in the same shop for >2 times in period 1 year. The result i want like:
table 1
cat1 number_of_user
ah 1
table 2
cat2 number_of_user
g 1
For total user, my query like:
WITH data_product AS(
SELECT DATE(payment_time) date,
user,
CONCAT(prod, "_", shop) product_shop,
cat1,
cat2
FROM
a
WHERE
DATE(payment_time) BETWEEN "2022-01-01" AND DATE_SUB(current_date, INTERVAL 1 day)
ORDER BY 1,2,3),
purchased AS (
SELECT user, product_shop, count(product_shop) tot_purchased
FROM data_product
GROUP BY 1,2
HAVING COUNT(product_shop) > 2
)
SELECT COUNT(user) number_of_user FROM purchased
Please help to get number of user buy the same product in the same shop more than 2 times in period based on cat1 and cat2.
Try this:
create temporary table table1 as(
select *,extract(YEAR from date) as year from `projectid.dataset.table`
);
create temporary table table2 as(
select * except(date,cat2) ,count(user) over(partition by cat1,year,user,prod,shop) tcount from table1
);
create temporary table table4 as(
select * except(date,cat1) ,count(user) over(partition by cat2,year,user,prod,shop) tcount from table1
);
select distinct year,cat1 ,count(distinct user) number_of_user from table2 where tcount>2 group by YEAR,cat1;
select distinct year,cat2 ,count(distinct user) number_of_user from table4 where tcount>2 group by YEAR,cat2;
If you want a single result set you can union both the select statements.
I think this query might work. The first part shows count of customers who purchased same product in category1 from same shop during one year. Second part shows that for category2, then we concatenate the two set by union operation :
with cte as
(select distinct
PDate,userID as userID,prod as prod,shop,cat1 as cat1,cat2,
count(userID) over (partition by UserID,prod,shop,year(Pdate),cat1) as cat1_count,
count(PDate) over (partition by UserID,prod,shop,year(Pdate),cat2) as cat2_count
from tbl1)
select
cte.cat1 as c1,'0' as c2,count(distinct cte.cat1) as Num
from cte
where cte.cat1_count>1
group by cte.prod,cte.userID,cte.cat1
union
select
'0',cte.cat2,count(distinct cte.cat2)
from cte
where cte.cat2_count>1
group by cte.prod,cte.userID,cte.cat2

Tips for Creating Summary Count of Value in other Tables

I have multiple tables with a status column in each. I want to display a summary of the counts of each status per table. Something like this:
=============================================
Status | Table A | Table B | Table C |
Status A | 3 | 8 | 2 |
Status B | 5 | 7 | 4 |
==============================================
I need help getting started as I'm not sure how to approach this issue. I can do simple COUNT functions like:
SELECT status, count(status) from TABLE_A group by status
But I'm not sure how to populate the data in the form I want or how to, if possible, use the table names as the column headers. I'd appreciate a point in the right direction. Thanks!
May be try doing left joins after you have calculated counts for each table separately.Something like:
select distinct t1.status,
count(t1.status) as [tableA],
t2.TableB,
t3.TableC from Table A t1
left join (
select distinct status,
count(status) as [TableB] from Table B
group by status
) t2 on t1.status=t2.status
left join (
select distinct status,
count(status) as [TableC] from Table C
group by status
) t3 on t1.status=t3.status
group by t1.Status
I would use union all and aggregation:
select status, sum(a) as a, sum(b) as b, sum(c) as c
from ((select status, count(*) as a, 0 as b, 0 as c
from tablea
group by status
) union all
(select status, 0, count(*), 0
from tableb
group by status
) union all
(select status, 0, 0, count(*)
from tablea
group by status
)
) abc
group by status;
This ensures that all rows appear, even when one or more tables are missing some values of status.
could be using left join
select t.status, a.cnt A, b.cnt B,c.cnt C
from(
select status
from tableA
union
select status
from tableB
select status
from tableC
) t
left join (
select status, count(*) cnt
from tableA
group by status
) a ON on t.status = a.status
left join (
select status, count(*) cnt
from tableB
group by status
) b ON on t.status = b.status
left join (
select status, count(*) cnt
from tableC
group by status
) c ON on t.status = c.status

Display result as group by count with max date?

I have below sample data, that I need to display results as count by group with max date.
REQUEST_NUMBER ASSIGNED_GROUP LAST_MODIFIED_DATE
001 GROUP A
001 GROUP B 2/2/2018
002 GROUP A
002 GROUP B 2/2/2018
002 GROUP C 2/3/2018
003 GROUP B
My expected result needs to be displayed as count of a group with max of last_modified_date only like:
ASSIGNED_GROUP TOTAL_COUNT
GROUP B 2
GROUP C 1
In my above example 001 was last assigned to GROUP B, 002 last assigned to GROUP C, and 003 is only 1 record with NULL last_modified_date, so remains with GROUP B.
I'm trying with just one result so far, but not getting proper results:
SELECT request_number, ASSIGNED_GROUP_NAME
FROM (
SELECT ROW_NUMBER() OVER (PARTITION BY request_number ORDER BY request_number) RNUM,
request_number, ASSIGNED_GROUP_NAME
FROM WORK_DETAIL
WHERE request_number = '3458112'
)
WHERE MAX(last_modified_date)
ORDER BY ASSIGNED_GROUP_NAME
Something like this could work
SELECT ASSIGNED_GROUP, COUNT(ASSIGNED_GROUP), MAX(LAST_MODIFIED_DATE) FROM YourTable
GROUP BY ASSIGNED_GROUP
You could use group by;
select t.assigned_group,t.last_modified_date,count(*) from table t inner join
(
select assigned_group,max(last_modified_date) as maxDate from table
where last_modified_date is not null
group by assigned_group
) t2
ON t.last_modified_date = t2.maxDate and t.assigned_group = t2.assigned_group
group by t.assigned_group,t.last_modified_date
You could use ajoin with a subquery with max_date group by assigned_group
select a.ASSIGNED_GROUP, count(*)
from my_table a
inner join(
select ASSIGNED_GROUP, max(LAST_MODIFIED_DATE) as max_date
from my_table
where LAST_MODIFIED_DATE is not null
group by ASSIGNED_GROUP
) t on t.max_date = a.LAST_MODIFIED_DATE and t.ASSIGNED_GROUP = a.ASSIGNED_GROUP
group by a.assigned_group

Merge duplicate rows

I have a Customer table which contains an ID and Email field. I've written the following query to return all duplicate Customers with the same Email:
SELECT ID, Email
FROM Customer a
WHERE EXISTS (SELECT 1
FROM Customer b
WHERE a.Email = b.Email
GROUP BY Email
HAVING COUNT(Email) = 2)
ORDER BY Email
This is returning records that look like the following:
ID Email
1 a#hotmail.com
2 a#hotmail.com
3 b#gmail.com
4 b#gmail.com
While this works, I actually need the data in the following format:
ID1 Email1 ID2 Email2
1 a#hotmail.com 2 a#hotmail.com
3 b#gmail.com 4 b#gmail.com
What is the best way to achieve this?
One method is conditional aggregation . . . assuming you have at most two emails:
select max(case when seqnum = 1 then id end) as id_1,
email as email_1,
max(case when seqnum = 2 then id end) as id_2,
email as email_2
from (select t.*, row_number() over (partition by email order by id) as seqnum
from t
) t
group by email;
Actually, why not just do:
select email, count(*) as num_dups, min(id) as id_1,
(case when count(*) > 1 then max(id) end) as id_2
from t
group by email;
Try:
SELECT MIN(ID) ID, Email, MAX(ID) ID2, Email AS EMAIL2
FROM Customer GROUP BY Email
if you want HAVING COUNT(Email) = 2, it will be like this
SELECT MIN(ID) ID, Email, MAX(ID) ID2, Email AS EMAIL2
FROM Customer GROUP BY Email
HAVING COUNT(Email) = 2
Your layout assumes that you can only have a total of 2 duplicates.
Maybe list the IDs instead like below?
declare #Duplicates table (Email varchar(50), Customers varchar(100))
insert #Duplicates select Email, '' from Customer group by Email having count(*) > 1
UPDATE d
SET
Customers= STUFF(( SELECT ','+ cast(ID as varchar(10))
FROM Customer c
WHERE c.Email = d.Email
FOR XML PATH(''), TYPE).value('.','VARCHAR(max)'), 1, 1, '')
FROM #Duplicates AS d
select * from #Duplicates
order by Email

Sql select count of sent and received

I need to write a query for report for sent and received transactions.
Status_Id Status_dt Status
1 4/1/2013 sent
1 4/1/2013 sent
2 4/2/2013 sent
3 4/3/2013 sent
1 4/1/2013 Received
1 4/4/2013 Received
2 4/4/2013 received
The transactions which are sent on particular date can be received on any date.
From above
on 4/1/2013 transactions sent were two (for id 1) and for this id wch are sent on 4/1/2013 have received on 4/1/2013 and 4/4/2013
so o/p should be
dt sent_count received_count
4/1/2013 2 2
on 4/2/2013 transactions sent were one (for id 2) and for this id wch are sent on 4/2/2013 have received on 4/4/2013
so o/p should be
dt sent_count received_count
4/2/2013 1 1
on 4/3/2013 transactions sent were one (for id 3) and for this id wch are sent on 4/3/2013 has not received yet
so o/p should be
dt sent_count received_count
4/3/2013 1 0
So if i run a query on 4/5/2013 the output should be:::
dt sent_count received_count
4/1/2013 2 2
4/2/2013 1 1
4/3/2013 1 0
for sent count i can write query as:
select status_dt, count(*)
from table
where status = 'sent'
group by status_dt
what query should i write for received count?
For the received, you need to join back to the sent to get the date. If the status ids were unique, this would be the query:
select t.status_dt, count(*)
from table t join
table tres
on t.status_id = tres.status_id and
t.status = 'sent' and
tres.status = 'received'
group by status_dt;
Instead, one way is to assign a unique id:
select t.status_dt, count(*) as SentCount, count(tres.status_id) as ReceivedCount
from (select t.*,
row_number() over (partition by status_id order by status_dt) as seqnum
from table t
where tres.status = 'sent'
) t join
(select tres.*,
row_number() over (partition by status_id order by status_dt) as seqnum
from table tres
where tres.status = 'received'
) tres
on t.status_id = tres.status_id and
t.seqnum = tres.seqnum
group by status_dt;
This unique id enumerates everything with a given status_id based on the date in the record (separately for sent and received). This works, because the received are always after the sent. So, the nth receive is always after the nth send.
If you want both SentCount and ReceivedCount in one query:
select t.status_dt, count(*) as SentCount, count(tres.status_id) as ReceivedCount
from (select t.*,
row_number() over (partition by status_id order by status_dt) as seqnum
from table t
where tres.status = 'sent'
) t left outer join
(select tres.*,
row_number() over (partition by status_id order by status_dt) as seqnum
from table tres
where tres.status = 'received'
) tres
on t.status_id = tres.status_id and
t.seqnum = tres.seqnum
group by status_dt;
Try this,
select t1.Status_dt,
(select count(t2.Status) from table t2 where t2.Status='sent' and t1.Status_dt = t2.Status_dt) as sent_count,
(select count(t2.Status) from table t2 where t2.Status='received' and t1.Status_dt = t2.Status_dt) as received_count
from table t1 group by t1.Sattus_dt