Sql select count of sent and received - sql

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

Related

row_number based on case when with text content

Here is a task I need to get three elements based on the given conditions:
three elements: user_id, order_time, ordered_subject
each unique user_id
earliest order_time
ordered_subjects' order should be app-> acc ->ayy
if there are several order_time are the same, you should take only one subject followed by the 3rd requirement
original table: user_order
user_id
order_time
ordered_subject
1
2001-02-09
app
2
2001-02-09
app
3
2001-02-10
ayy
1
2001-02-09
acc
1
2001-02-10
app
4
2001-02-08
ayy
5
2001-02-09
acc
5
2001-02-09
ayy
expected table:
user_id
order_time
ordered_subject
1
2001-02-09
app
2
2001-02-09
app
3
2001-02-10
ayy
4
2001-02-08
ayy
5
2001-02-09
acc
I come up with the idea of case when and row_number() over, but it doesn't work
the code I tried:
select
a.uid,
a.subject,
b.min_time,
(case when "app" then 1
when "acc" then 2
when "ayy" then 3
else 4 end) as rn,
row_number() over(partition by
concat(uid,order_id)
order by
rn)
from (
select uid, min(order_time) as min_time
from user_order
group by
uid
) as b
-- join
user_order as a
-- on
where
a.uid = b.uid
and
b.min_time = a.order_time
How should I fix this?
You want one result row per user. Per user you want the earliest order and if there is more than one order on the earliest date you prefer the order subject app over acc and acc over ayy.
You want to use ROW_NUMBER, so partition by user ID and order by date and the order subject in the desired order.
select user_id, order_time, ordered_subject
from
(
select
user_id, order_time, ordered_subject,
row_number() over
(partition by user_id
order by order_time,
case ordered_subject
when 'app' then 1
when 'acc' then 2
when 'ayy' then 3
else 4
end) as rn
from mytable
) numbered
where rn = 1
order by user_id;

SQL: How do I display all records per unique id, but not the first record ever recorded in SQL

Example:
id Pricemoney time/date
1 100 01/20/2017
1 10 01/21/2017
1 1000 01/21/20147
2 10 01/23/2017
2 100 01/24/2017
3 1000 01/19/2017
3 100 01/22/2017
3 10 01/24/2017
I want to run a SQL query where I can display all the Id and it's pricemoney BUT NOT include the first record (based on time/date) per unique
Just to clarify what I do not want to be displayed
userid Pricemoney issuedate
1 100 01/20/2017 -- not included
1 10 01/21/2017
1 1000 01/21/20147
2 10 01/23/2017 --- not inlcuded
2 100 01/24/2017
3 1000 01/19/2017 -- not included
3 100 01/22/2017
3 10 01/24/2017
Expected result:
id Pricemoney time/date
1 10 01/21/2017
1 1000 01/21/20147
2 100 01/24/2017
3 100 01/22/2017
3 10 01/24/2017
You can use row_number():
select t.*
from (select t.*,
row_number() over (partition by id order by time_date asc) as seqnum
from <tablename> t
) t
where seqnum > 1;
If you want to keep single rows, you can do:
select t.*
from (select t.*,
row_number() over (partition by id order by time_date asc) as seqnum,
count(*) over (partition by id) as cnt
from <tablename> t
) t
where seqnum > 1 and cnt > 1;
You may use EXISTS
select t1.*
from data t1
where exists (
select 1
from data t2
where t1.id = t2.id and t2.time_date < t1.time_date
)
you can try this :
select data1.id,data1.Date,data1.Pricemoney from data1
left join (
select id ,min(Date) date from data1
group by id
) as t
on data1.date= t.date and t.id = data1.id
where t.id is null
group by data1.id,data1.Date,data1.Pricemoney
above query not duplicated records also ignore, if want
not duplicated records then use having count(id) > 1 in left query e,g.
select data1.id,data1.Date,data1.Pricemoney from data1
left join (
select id ,min(Date) date from data1
group by id
having COUNT(id) > 1
) as t
on data1.date= t.date and t.id = data1.id
where t.id is null
group by data1.id,data1.Date,data1.Pricemoney

count consecutive statuses from each ID

I am trying to find a list of clients that have at least 3 consecutive items that are "processed". The following is what my table looks like:
ClientID ItemID Status
1 1 Pending
1 2 Processed
1 3 Processed
2 4 Processed
2 5 Processed
1 6 Processed
1 7 Pending
2 8 Pending
2 9 Processed
3 10 Pending
3 11 Pending
2 12 Processed
3 13 Pending
2 14 Processed
1 15 Processed
2 16 Processed
Expected results:
1 (since it had 3 consecutive processed records from 2, 3, 6 )
2 (since it had 4 consecutive processed records from 9, 12, 14, 16)
As you can see, I define "consecutive" as the next record with the same ClientID and not as the next record in the table, this is what I am having trouble with. My counter restarts when the next clientid in the table is different.
my attempt:
WITH count
AS
(
SELECT *, COUNT(1) OVER(PARTITION BY clientid, count) NotPending
FROM (
SELECT *, (
SELECT COUNT(ItemId)
FROM ##temp a
WHERE status like '%pend%'
AND ItemId < b.ItemId) AS count
FROM ##temp b
WHERE status not like '%pend%'
) t1
)
SELECT distinct clientid from count where NotPending >= 3
You can use row_number() to place rows with the same consecutive status in the same group:
select *,
row_number() over (partition by ClientId order by ItemId)
- row_number() over (partition by ClientId, ItemStatus order by ItemId) as groupName
from Table1
order by ClientId, ItemId
Then you can count the number of entries per group:
select distinct ClientId, count(*) from (
select *,
row_number() over (partition by ClientId order by ItemId)
- row_number() over (partition by ClientId, ItemStatus order by ItemId) as groupName
from Table1
) t
where ItemStatus = 'Processed'
group by ClientId, groupName
having count(*) >= 3
Demo
In your example there are no clients with more than 5 consecutive items (and in your Select you check for >= 10).
Looking for 3 items, returns 1 & 2 for your example data:
WITH cte AS
(
SELECT ClientID, ItemID, Status,
-- returns 3 when there's only 'Processed'
Sum(CASE WHEN Status = 'Processed' THEN 1 end)
Over (PARTITION BY ClientID
ORDER BY ItemId
-- 3 rows including current row
ROWS 2 Preceding) AS Cnt
FROM ##temp
)
SELECT DISTINCT ClientID
FROM cte
WHERE Cnt = 3

How do I select records where a value has not been exceeded

I wish to extract from a database a list of job Ids where the status for each job has not exceeded a given criteria.
For this example table I want to display all the jobid that have not exceeded status 200 and display only the latest status.
Job-Progress table
Jobid Status Date Time
1234 100 20131001 080000
1234 200 20131001 100000
1234 300 20131001 140000
9876 100 20131014 110000
5555 100 20131015 100000
5555 200 20131016 080000
The result i am looking for is
Jobid Status Date Time
9876 100 20131014 110000
5555 200 20131016 080000
Database is on AS400
This is a good use of window/analytic functions.
You can use row_number() to get the most recent status. You can use sum() over to count the number of times that status exceeds 200.
select jobid, Status, Date, Time
from (select jp.*,
row_number() over (partition by jobid order by date desc, time desc) as seqnum,
sum(case when status >= 200 then 1 else 0 end) over (partition by jobid) as status_200
from JobProgress jp
) jp
where status_200 = 0 and seqnum = 1;
The where clause then filters to the rows you are looking for.
select t1.*
from your_table t1
inner join
(
select Jobid, max(date*1000000+time) as maxdt
from your_table
group by jobid
having sum(case when status > 200 then 1 else 0 end) = 0
) t2 on t1.jobid = t2.jobid
and t1.date*100000+t1.time = maxdt
This is the simplest approach I can think of:
SELECT t.* FROM t
INNER JOIN (
SELECT jobid, max(date) date FROM t
GROUP BY jobid
HAVING COUNT(CASE WHEN status > 200 THEN 1 END) = 0
) s
ON t.jobid = s.jobid AND t.date = s.date
Fiddle here.
A common table expression will allow you to employ the ROW_NUMBER() function to mark the latest row of each job as 1. Then all you need to do is pick that row whenever its status is acceptable.
With x as
(SELECT *,
ROW_NUMBER() OVER(PARTITION BY Jobid ORDER BY Date, time DESC) AS pick
FROM JobProgress
)
SELECT jobid, status, date, time
FROM x
WHERE Status <= 200
And pick = 1
In SQL server you can use ROW_NUMBER:
SELECT *
FROM (SELECT
Jobid,
status,
ROW_NUMBER() OVER(PARTITION BY Jobid ORDER BY Date DESC) AS rn
FROM
Job-Progress
WHERE Status < 200) A
WHERE RowNum = 1

SQL Server query + joining results

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;