SQL: How do I display all records per unique id, but not the first record ever recorded in SQL - 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

Related

SELECT statement that shows continuous data with condition

I consider myself good at SQL but failed at this problem.
I need a SELECT statement that shows all rows above 100 if there are
3 rows or more with 100 next to it.
Given Table "Trend":
| id | volume |
+----+---------+
| 0 | 200 |
| 1 | 90 |
| 2 | 101 |
| 3 | 120 |
| 4 | 200 |
| 5 | 10 |
| 6 | 400 |
I need a SELECT statement to produce:
| 2 | 101 |
| 3 | 120 |
| 4 | 200 |
I suspect that you are after the following logic:
select *
from (
select t.*,
sum(case when volume > 100 then 1 else 0 end) over(order by id rows between 2 preceding and 2 following) cnt
from mytable t
) t
where volume > 100 and cnt >= 3
This counts how many values are above 100 in the range made of the two preceding rows, the current row and the next two rows. Then we filter on rows whose window count is 3 or more.
This uses a syntax that most database support (provided that window functions are available). Neater expressions may be available depending on the actual database you are using.
In MySQL:
sum(volume > 100) over(order by id rows between 2 preceding and 2 following) cnt
In Postgres:
count(*) filter(where volume > 100) over(order by id rows between 2 preceding and 2 following) cnt
Or:
sum((volume > 100)::int) over(order by id rows between 2 preceding and 2 following) cnt
This is tricky because you want the original rows . . . I am going to suggest lag() and lead():
select id, volume
from (select t.*,
lag(volume, 2) over (order by id) as prev_volume_2,
lag(volume) over (order by id) as prev_volume,
lead(volume, 2) over (order by id) as next_volume_2,
lead(volume) over (order by id) as next_volume
from t
) t
where volume > 100 and
( (prev_volume_2 > 100 and prev_volume > 100) or
(prev_volume > 100 and next_volume > 100) or
(next_volume_2 > 100 and next_volume > 100)
);
Another method is to treat this as a gaps-and-islands problem. This makes the solution more generalizable. You can assign a group by counting the number of rows less than or equal to 100 up to each row. Then count the number that are greater than 100 to see if those groups qualify to be in the final results:
select id, volume
from (select t.*,
sum(case when volume > 100 then 1 else 0 end) over (partition by grp) as cnt
from (select t.*,
sum(case when volume <= 100 then 1 else 0 end) over (order by id) as grp
from t
) t
) t
where volume > 100 and cnt >= 3;
Here is a db<>fiddle with these two approaches.
Key point here is "3 rows or more". MATCH_RECOGNIZE could be used:
SELECT *
FROM trend
MATCH_RECOGNIZE (
ORDER BY id -- ordering of a streak
MEASURES FINAL COUNT(*) AS l -- count "per" match
ALL ROWS PER MATCH -- get all rows
PATTERN(a{3,}) -- 3 or more
DEFINE a AS volume >= 100 -- condtion of streak
)
ORDER BY l DESC FETCH FIRST 1 ROWS WITH TIES;
-- choose the group that has the longest streak
The strength of this approach is a PATTERN part which could be modifed to handle different scenarios like a{3,5} - between 3 and 5 occurences, a{4} exactly 4 occurences and so on. More conditions could be defined which allows to build complex pattern detection.
db<>fiddle demo
Get the min value of volume for all consecutive 3 rows of the table.
Then join to the table and keep only the ones belonging to a group that has min > 100:
select distinct t.*
from Trend t
inner join (
select t.*,
min(t.volume) over (order by t.id rows between current row and 2 following) min_volume,
lead(t.id, 1) over (order by t.id) next1,
lead(t.id, 2) over (order by t.id) next2
from Trend t
) m on t.id in (m.id, m.next1, m.next2)
where m.min_volume > 100 and m.next1 is not null and m.next2 is not null
See the demo for SQL Server, MySql, Postgresql, Oracle, SQLite.
Results:
> id | volume
> -: | -----:
> 2 | 101
> 3 | 120
> 4 | 200
a simplistic approach:
--CREATE TABLE Trend (id integer, volume integer);
--insert into Trend VALUES
-- (0,200),
-- (1,90),
-- (2,101),
-- (3,120),
-- (4,200),
-- (5,10),
-- (6,400);
SELECT
t1.id, t1.volume
--,t2.id, t2.volume
--,t3.id, t3.volume
FROM Trend t1
INNER JOIN Trend t2 ON t2.id>t1.id and t2.volume>100 and not exists (select * from Trend t5 where t5.id between t1.id+1 and t2.id-1)
INNER JOIN Trend t3 ON t3.id>t2.id and t3.volume>100 and not exists (select * from Trend where id between t2.id+1 and t3.id-1)
WHERE t1.volume>100
union all
SELECT
--t1.id, t1.volume
t2.id, t2.volume
--,t3.id, t3.volume
FROM Trend t1
INNER JOIN Trend t2 ON t2.id>t1.id and t2.volume>100 and not exists (select * from Trend t5 where t5.id between t1.id+1 and t2.id-1)
INNER JOIN Trend t3 ON t3.id>t2.id and t3.volume>100 and not exists (select * from Trend where id between t2.id+1 and t3.id-1)
WHERE t1.volume>100
union all
SELECT
--t1.id, t1.volume
--t2.id, t2.volume
t3.id, t3.volume
FROM Trend t1
INNER JOIN Trend t2 ON t2.id>t1.id and t2.volume>100 and not exists (select * from Trend t5 where t5.id between t1.id+1 and t2.id-1)
INNER JOIN Trend t3 ON t3.id>t2.id and t3.volume>100 and not exists (select * from Trend where id between t2.id+1 and t3.id-1)
WHERE t1.volume>100

how to return the max seqence record

I've a table that stores the historical data, what i'm trying to do is trying to capture the max seq record. i can do that, but i need to include the tr_type, then i'll use the outupt to join with another table. below is ex of my data
CLM_NO SEQ SUB TR_TYPE
12345 1 1 50
12345 1 2 50
12345 2 1 60
12345 2 2 60
i want to return only the last row
You can try to use exists and correlated subquery
SELECT *
FROM T t1
WHERE exists(
SELECT 1
FROM T tt
GROUP BY tt.CLM_NO
HAVING MAX(tt.SEQ) = t1.SEQ AND MAX(tt.SUB) = t1.SUB
)
EDIT
You can try to use ROW_NUMBER window function.
SELECT * FROM (
SELECT *,ROW_NUMBER() OVER(PARTITION BY CLM_NO ORDER BY TRAN_SEQ DESC,TRAN_SUB DESC) rn
FROM TBL t1
)t1
where rn = 1

How to select unique records by ORACLE

When I perform "SELECT * FROM table" I got results like below:
ID Date Time Type
----------------------------------
60 03/03/2013 8:55:00 AM 1
60 03/03/2013 2:10:00 PM 2
110 17/03/2013 9:15:00 AM 1
67 24/03/2013 9:00:00 AM 1
67 24/03/2013 3:05:00 PM 2
as you see each ID has a transaction Type 1 and 2 in the same Date
except ID 110 HAS only Type 1
So how could I just get result like this:
ID Date Time Type
----------------------------------
110 17/03/2013 9:15:00 AM 1
as only one record are returned from the first result
Change the partition definition (partition by id,date) according to your needs
select *
from (select t.*
,count(*) over (partition by id,date) as cnt
from mytable t
) t
where t.cnt = 1
;
You can use this:
select * from my_table t
where exists (
select 1 from my_table
where id = t.id
group by id
having count(*) = 1
)
If you want only type 1, then compare the minimum and maximum values. I prefer window functions:
select t.*
from (select t.*, min(type) over (partition by id) as mintype,
max(type) over (partition by id) as maxtype
from t
) t
where mintype = maxtype and mintype = 1;
If you want only records of the same type (and not specifically type = 1), then remove that condition.
If you want only records on the same day, then include the date in the partition by.
Under some circumstances, not exists can be faster:
select t.*
from t
where not exists (select 1 from t t2 where t2.id = t.id and t2.type <> 1);

Count consecutive duplicate values in SQL

I have a table like so
ID OrdID Value
1 1 0
2 2 0
3 1 1
4 2 1
5 1 1
6 2 0
7 1 0
8 2 0
9 2 1
10 1 0
11 2 0
I want to get the count of consecutive value where the value is 0. Using the example above the result will be 3 (Rows 6, 7 and 8). I am using sql server 2008 r2.
I am going to presume that id is unique and increasing. You can get counts of consecutive values by using the different of row numbers. The following counts all sequences:
select grp, value, min(id), max(id), count(*) as cnt
from (select t.*,
(row_number() over (order by id) - row_number() over (partition by value order by id)
) as grp
from table t
) t
group by grp, value;
If you want the longest sequence of 0s:
select top 1 grp, value, min(id), max(id), count(*) as cnt
from (select t.*,
(row_number() over (order by id) - row_number() over (partition by value order by id)
) as grp
from table t
) t
group by grp, value
having value = 0
order by count(*) desc
A query using not exists to find consecutive 0s
select top 1 min(t2.id), max(t2.id), count(*)
from mytable t
join mytable t2 on t2.id <= t.id
where not exists (
select 1 from mytable t3
where t3.id between t2.id and t.id
and t3.value <> 0
)
group by t.id
order by count(*) desc
http://sqlfiddle.com/#!3/52989/3

Any other alternative to write this SQL query

I need to select data base upon three conditions
Find the latest date (StorageDate Column) from the table for each record
See if there is more then one entry for date (StorageDate Column) found in first step for same ID (ID Column)
and then see if DuplicateID is = 2
So if table has following data:
ID |StorageDate | DuplicateTypeID
1 |2014-10-22 | 1
1 |2014-10-22 | 2
1 |2014-10-18 | 1
2 |2014-10-12 | 1
3 |2014-10-11 | 1
4 |2014-09-02 | 1
4 |2014-09-02 | 2
Then I should get following results
ID
1
4
I have written following query but it is really slow, I was wondering if anyone has better way to write it.
SELECT DISTINCT(TD.RecordID)
FROM dbo.MyTable TD
JOIN (
SELECT T1.RecordID, T2.MaxDate,COUNT(*) AS RecordCount
FROM MyTable T1 WITH (nolock)
JOIN (
SELECT RecordID, MAX(StorageDate) AS MaxDate
FROM MyTable WITH (nolock)
GROUP BY RecordID)T2
ON T1.RecordID = T2.RecordID AND T1.StorageDate = T2.MaxDate
GROUP BY T1.RecordID, T2.MaxDate
HAVING COUNT(*) > 1
)PT ON TD.RecordID = PT.RecordID AND TD.StorageDate = PT.MaxDate
WHERE TD.DuplicateTypeID = 2
Try this and see how the performance goes:
;WITH
tmp AS
(
SELECT *,
RANK() OVER (PARTITION BY ID ORDER BY StorageDate DESC) AS StorageDateRank,
COUNT(ID) OVER (PARTITION BY ID, StorageDate) AS StorageDateCount
FROM MyTable
)
SELECT DISTINCT ID
FROM tmp
WHERE StorageDateRank = 1 -- latest date for each ID
AND StorageDateCount > 1 -- more than 1 entry for date
AND DuplicateTypeID = 2 -- DuplicateTypeID = 2
You can use analytic function rank , can you try this query ?
Select recordId from
(
select *, rank() over ( partition by recordId order by [StorageDate] desc) as rn
from mytable
) T
where rn =1
group by recordId
having count(*) >1
and sum( case when duplicatetypeid =2 then 1 else 0 end) >=1