Get next value for each of values from next table CTE - sql

I have the following table:
dbo.split
Name Time
Alex 120
John 80
John 300
Mary 500
Bob 900
And then another table dbo.travel
Name Time
Alex 150
Alex 160
Alex 170
John 90
John 100
John 310
Mary 550
Mary 600
Mary 499
Bob 800
Bob 700
For each value in table split I need to find the next value in table travel. I tried to do it with CTE a with ROW_NUMBER() to get next by group, but there's no way I can group by correct value, since dbo.split can containt multiple values for the same name.
I'm looking for the following output:
Name Time TravelTime
Alex 120 150
John 80 90
John 300 310
Mary 500 550
Bob 900 NULL
Here's what I have so far but it fails because split table can have multiple records per person:
;with result as (
select t.*,
ROW_NUMBER() OVER (Partition BY t.Name order by t.Time) as rn
from travel t join split s
on t.Name = s.Name and t.TIME>s.Time
)

I would use apply:
select s.*, t.time
from split s outer apply
(select top (1) t.*
from travel t
where t.name = s.name and t.time > s.time
order by t.time asc
) t;
In this case, apply is doing essentially the same thing as a correlated subquery, so you could phrase it that way as well.

You can try as below
Select * from(Select
Name,t.time,t1.time,
Row_number() over (partition by
Name,t.time order by t1.time) rn
from split t
Join travel t1 on t.time <t1.time and
t.name =t1.name)
where
rn=1;

Related

SQL select specific group from table

I have a table named trades like this:
id trade_date trade_price trade_status seller_name
1 2015-01-02 150 open Alex
2 2015-03-04 500 close John
3 2015-04-02 850 close Otabek
4 2015-05-02 150 close Alex
5 2015-06-02 100 open Otabek
6 2015-07-02 200 open John
I want to sum up trade_price grouped by seller_name when last (by trade_date) trade_status was 'open'. That is:
sum_trade_price seller_name
700 John
950 Otabek
The rows where seller_name is Alex are skipped because the last trade_status was 'close'.
Although I can get desirable output result with the help of nested select
SELECT SUM(t1.trade_price), t1.seller_name
WHERE t1.seller_name NOT IN
(SELECT t2.seller_name FROM trades t2
WHERE t2.seller_name = t1.seller_name AND t2.trade_status = 'close'
ORDER BY t2.trade_date DESC LIMIT 1)
from trades t1
group by t1.seller_name
But it takes more than 1 minute to execute above query (I have approximately 100K rows).
Is there another way to handle it?
I am using PostgreSQL.
I would approach this with window functions:
SELECT SUM(t.trade_price), t.seller_name
FROM (SELECT t.*,
FIRST_VALUE(trade_status) OVER (PARTITION BY seller_name ORDER BY trade_date desc) as last_trade_status
FROM trades t
) t
WHERE last_trade_status <> 'close;
GROUP BY t.seller_name;
This should perform reasonably with an index on seller_name
select
sum(trade_price) as sum_trade_price,
seller_name
from
trades
inner join
(
select distinct on (seller_name) seller_name, trade_status
from trades
order by seller_name, trade_date desc
) s using (seller_name)
where s.trade_status = 'open'
group by seller_name

SUM GROUP BY giving undesired result

First 12 rows of Table T1:
Name Status Duration
Todd Active 60
Todd Active 60
Todd Active 60
Todd Schedu 60
Todd Schedu 60
Todd Schedu 120
Todd Schedu 120
Bran Active 30
Bran Active 30
Bran Active 60
Bran No Show 120
Bran No Show 120
If I run this query (or use a DISTINCT without the GROUP BY):
SELECT Name, Status, Duration
FROM Table T1
GROUP BY Name,Status,Duration
I get:
Name Status Duration
Todd Active 60
Todd Schedu 60
Todd Schedu 120
Bran Active 30
Bran Active 60
Bran No Show 120
From the above result, I want the desired result as SUM(Duration) GROUPED BY Name, Status:
Name Status Duration
Todd Active 60
Todd Schedu 180
Bran Active 90
Bran No Show 120
I'm trying this query to achieve the desired result:
SELECT Name, Status, SUM(Duration)
FROM Table T1
GROUP BY Name,Status
But I'm getting huge numbers for SUM(Duration) - It's probably adding all the durations and not the distinct durations for each group of Name and Status.
One method to get what you want uses a subquery:
SELECT Name, Status, SUM(Duration)
FROM (SELECT Name, Status, Duration
FROM Table T1
GROUP BY Name,Status,Duration
) NSD
GROUP BY Name, Status;
You can use Distinct inside SUM function. It will give you expected result.
SELECT Name, Status, SUM(DISTINCT Duration)
FROM T1
GROUP BY Name,Status
You could use CTE,
WITH C1 AS(
SELECT Name, Status, Duration
FROM Table T1
GROUP BY Name,Status,Duration
)
SELECT Name,Status,SUM(Duration) FROM C1 GROUP BY Name,Status
with temp_cte
as
(select Name, Status, Duration
FROM dbo.test2
group by name,status,duration
)
select tc.name,tc.status,sum(tc.duration) from temp_cte as tc
group by tc.name,tc.status
order by name

Fetching same rows that has multiple columns along with other rows

I have a view which results the following rows.
comp Sub-comp Lognum id Firname LAstname
AK AK-G 0 3897 ABC DEF
AK AK-G 0 5432 mark ray
MC MC-A 0 1234 john steve
MC MC-A 0 5678 dan pitcher
MC MC-A 0 9843 james robin
MC MC-A 84 1234 john steve
MC MC-A 84 5678 dan pitcher
MC MC-A 84 9843 james robin
I want to fetch the only the rows that has a lognum (if the same row has 0 also as lognum) along with the other rows that has just 0 as lognum.
The result table should be like this
comp Sub-comp Lognum id Firname LAstname
AK AK-G 0 3897 ABC DEF
AK AK-G 0 5432 mark ray
MC MC-A 84 1234 john steve
MC MC-A 84 5678 dan pitcher
MC MC-A 84 9843 james robin
And the outline of the query is as follows
create view view1 as
select
comp, Sub-comp, "00" as Lognum, id ,Firname ,LAstname
from
table A
inner joins---
UNION
select
select
comp, Sub-comp, Lognum, id ,Firname ,LAstname from
table B
inner joins----
;
Can anyone help?
Thanks!
Try this:
select * from(
select comp,
Sub-comp,
Lognum,
id,
Firname,
LAstname,
row_number() over(partition by id order by lognum desc) rn
from table_name)
where rn = 1;
This will show the line with the biggest lognum grouped by the ID.
This query should work, even in cases where, for a given id value, you have multiple "non-zero" lognum rows.
If you look at the where clause, rows with non-zero lognum values are always returned (t.Lognum != 0). But rows with zero lognum values will also return, but only if the t.rn = 1 condition is true, which will only happen if there aren't any other non-zero lognums for that same id (see the order by clause of the row_number() window function).
select t.comp,
t.Sub-comp,
t.Lognum,
t.id,
t.Firname,
t.LAstname
from (select t.*,
row_number() over (
partition by t.id
order by case when t.lognum = 0 then 1 else 0 end) as rn
from your_view t) t
where t.Lognum != 0 or t.rn = 1

How to Sum the 1st record of one column with the 2nd record of another column?

I am trying the Sum the 2nd record of one column with the 1st record of another column and store the result in a new column
Here is the example SQL Server table
Emp_Code Emp_Name Month Opening_Balance
G101 Sam 1 1000
G102 James 2 -2500
G103 David 3 3000
G104 Paul 4 1800
G105 Tom 5 -1500
I am trying to get the output as below on the new Reserve column
Emp_Code Emp_Name Month Opening_Balance Reserve
G101 Sam 1 1000 1000
G102 James 2 -2500 -1500
G103 David 3 3000 1500
G104 Paul 4 1800 3300
G105 Tom 5 -1500 1800
Actually the rule for calculating the Reserve column is that
For Month-1 it's the same as Opening Balance
For rest of the months its Reserve for Month-2 = Reserve for Month-1 + Opening Balance for Month-2
You seem to want a cumulative sum. In SQL Server 2012+, you would do:
select t.*,
sum(opening_balance) over (order by [Month]) as Reserve
from t;
In earlier versions, you would do this with a correlated subquery or apply:
select t.*,
(select sum(t2.opening_balance) from t t2 where t2.[Month] <= t.[Month]) as reserve
from t;
You can do a self join.
SELECT t.Emp_Code, t.Emp_Name, t.Month, t.Opening_Balance, t.Opening_Balance + n.Reserve
FROM Table1 t
JOIN Table2 n
ON t.Month = n.Month - 1

sql that identifies which account numbers have multiple agents

I dont think a count will work here, can someone help me get an sql that identifies which account numbers have multiple agents, more than two agents in the where condition.
AGENT_NAME ACCOUNT_NUMBER
Clemons, Tony 123
Cipollo, Michael 123
Jepsen, Sarah 567
Joanos, James 567
McMahon, Brian 890
Novak, Jason 437
Ralph, Melissa 197
Reitwiesner, John 221
Roman, Marlo 123
Rosenzweig, Marcie 890
Results should be something like this.
ACCOUNT_NUMBER AGENT_NAME
123 Cipollo, Michael
123 Roman, Marlo
123 Clemons, Tony
890 Rosenzweig, Marcie
890 McMahon, Brian
567 Joanos, James
567 Jepsen, Sarah
You can do this using window functions:
select t.account_number, t.agent_name
from (select t.*, min(agent_name) over (partition by account_number) as minan,
max(agent_name) over (partition by account_number) as maxan
from table t
) t
where minan <> maxan;
If you know the agent names are never duplicated, you could just do:
select t.account_number, t.agent_name
from (select t.*, count(*) over (partition by account_number) as cnt
from table t
) t
where cnt > 1;
Assuming your table name is test, this should pull all the records with duplicate ACCOUNT_NUMBER:
select * from test where ACCOUNT_NUMBER in
(select ACCOUNT_NUMBER from test
group by ACCOUNT_NUMBER having
count(ACCOUNT_NUMBER)>1)
order by ACCOUNT_NUMBER
Using count function u can get the result
CREATE TABLE #TEMP
(
AGENT_NAME VARCHAR(100),
ACCOUNT_NUMBER INT
)
INSERT INTO #TEMP
VALUES ('CLEMONS, TONY',123),
('CIPOLLO, MICHAEL',123),
('JEPSEN, SARAH',567),
('JOANOS, JAMES',567),
('MCMAHON, BRIAN',890),
('NOVAK, JASON',437),
('RALPH, MELISSA',197),
('REITWIESNER, JOHN',221),
('ROMAN, MARLO',123),
('ROSENZWEIG, MARCIE',890)
SELECT a.ACCOUNT_NUMBER,a.AGENT_NAME
FROM #TEMP A
JOIN(SELECT COUNT(1) CNT,
ACCOUNT_NUMBER
FROM #TEMP
GROUP BY ACCOUNT_NUMBER) B
ON A.ACCOUNT_NUMBER = B.ACCOUNT_NUMBER
WHERE B.CNT != 1