Select data from Sybase database but only select the row with the highest sequence - sql

I'm trying to select data from my database from the highest sequence number, I have been struggling with this for a while and cant get it to work.
The database has a lot of Columns with data. I only want data from the row with the highest sequence number to search in, because the data from lower sequences is not of any value for me. Unfortunately the rows from the lower sequences can not be deleted.
Database looks like this:
-----------------------------
| ID | SEQ | rest of the data
-----------------------------
| 1 | 1 | ..
| 1 | 2 | ....
| 2 | 1 | ..
| 1 | 3 | ....
| 3 | 1 | ..
| 1 | 2 | ....
| 4 | 1 | ........
My question is, how can i select only the ID's with the highest sequence number and search in those rows with the WHERE clause?

On oracle11g you can use:
SELECT *
FROM (
SELECT YOUR_TABLE.*, RANK() OVER (PARTITION BY ID oRDER BY SEQ DESC) RN
FROM YOUR_TABLE) A
WHERE RN=1;

SELECT *
FROM (
SELECT t.*,
ROW_NUMBER() OVER ( PARTITION BY ID ORDER BY SEQ DESC ) AS rn
FROM your_table t
)
WHERE rn = 1
or
SELECT ID,
MAX( seq ) AS seq,
MAX( other_column_1 ) KEEP ( DENSE_RANK LAST ORDER BY seq ) AS other_column_1,
MAX( other_column_2 ) KEEP ( DENSE_RANK LAST ORDER BY seq ) AS other_column_2
-- ...
FROM your_table
GROUP BY id
or
SELECT *
FROM your_table t
WHERE seq IN ( SELECT MAX( seq )
FROM your_table x
WHERE x.id = t.id )
or
SELECT t.*
FROM your_table t
INNER JOIN ( SELECT id, MAX( seq ) AS seq
FROM your_table
GROUP BY id ) x
ON ( x.id = t.id AND x.seq = t.seq )

Related

With some WHERE clause criteria, have SQL output go to next line

I am trying to write a query where I have some criteria where I pivot the results. However, due to output file constraints I am looking for the output to create a new line after the pivot exceeds X, even if the ID and such is otherwise the same.
What I am trying to do:
|--ID--|-Value-|
| 1 | val1 |
| 1 | val2 |
| 1 | val3 |
| 2 | val1 |
|--ID--|-Col1-|-Col2-|
| 1 | Val1| Val2|
| 1 | Val3| |
| 2 | Val1| |
SELECT *
FROM table
PIVOT(max(value) for field1 in (t1,t2)
as pvt
ORDER BY UNIQUE_ID
This is just a pivot example to pivot this particular column. However the output has a very strict number of column requirement so I'd be looking for any pivot beyond the 5th to "overflow" to the next row while retaining the unique id. I am looking at PIVOT but I dont think it will work here.
Is this even possible within the Snowflake platform or do I need to explore other options?
This requirement is purely presentation matter and in my opinion should not be performed at the database level. With that being said it is possible to achieve it by numbering rows in group and performing modulo division:
Samle data:
CREATE OR REPLACE TABLE tab
AS
SELECT 1 AS id, 'val1' AS value UNION
SELECT 1 AS id, 'val2' AS value UNION
SELECT 1 AS id, 'val3' AS value UNION
SELECT 2 AS id, 'val1' AS value;
Query:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER(PARTITION BY id ORDER BY value) - 1 AS rn
FROM tab
)
SELECT
id
,MAX(CASE WHEN rn % 2 = 0 THEN value END) AS col1
,MAX(CASE WHEN rn % 2 = 1 THEN value END) AS col2
FROM cte
GROUP BY id, FLOOR(rn / 2)
ORDER BY id, FLOOR(rn / 2);
Intermediate result:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER(PARTITION BY id ORDER BY value) - 1 AS rn
FROM tab
)
SELECT id,value, rn, FLOOR(rn / 2) AS row_index, rn % 2 AS column_index
FROM cte
ORDER BY ID, rn;
Generalized:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER(PARTITION BY id ORDER BY value) - 1 AS rn
FROM tab
)
SELECT
id
,MAX(CASE WHEN rn % N = 0 THEN value END) AS col1
,MAX(CASE WHEN rn % N = 1 THEN value END) AS col2
-- ....
,MAX(CASE WHEN rn % N = N-1 THEN value END) AS colN
FROM cte
GROUP BY id, FLOOR(rn / N)
ORDER BY id, FLOOR(rn / N);

Get top 5 records for each group and Concate them in a Row per group

I have a table Contacts that basically looks like following:
Id | Name | ContactId | Contact | Amount
---------------------------------------------
1 | A | 1 | 12323432 | 555
---------------------------------------------
1 | A | 2 | 23432434 | 349
---------------------------------------------
2 | B | 3 | 98867665 | 297
--------------------------------------------
2 | B | 4 | 88867662 | 142
--------------------------------------------
2 | B | 5 | null | 698
--------------------------------------------
Here, ContactId is unique throughout the table. Contact can be NULL & I would like to exclude those.
Now, I want to select top 5 contacts for each Id based on their Amount. I am accomplished that by following query:
WITH cte AS (
SELECT id, Contact, amount, ROW_NUMBER()
over (
PARTITION BY id
order by amount desc
) AS RowNo
FROM contacts
where contact is not null
)
select *from cte where RowNo <= 5
It's working fine upto this point. Now I want to concate these (<=5) record for each group & show them in a single row by concatenating them.
Expected Result :
Id | Name | Contact
-------------------------------
1 | A | 12323432;23432434
-------------------------------
2 | B | 98867665;88867662
I am using following query to achieve this but it still gives all records in separate rows and also including Null values too:
WITH cte AS (
SELECT id, Contact, amount,contactid, ROW_NUMBER()
over (
PARTITION BY id
order by amount desc
) AS RowNo
FROM contacts
where contact is not null
)
select *from id, name,
STUFF ((
SELECT distinct '; ' + isnull(contact,'') FROM cte
WHERE co.id= cte.id and co.contactid= cte.contactid
and RowNo <= 5
FOR XML PATH('')),1, 1, '')as contact
from contacts co inner join cte where cte.id = co.id and co.contactid= cte.contactid
Above query still gives me all top 5 contacts in diff rows & including null too.
Is it a good idea to use CTE and STUFF togather? Please suggest if there is any better approach than this.
I got the problem with my final query:
I don't need original Contact table in my final Select, since I already have everything I needed in CTE. Also, Inside STUFF(), I'm using contactid to join which is what actually I'm trying to concat here. Since I'm using that condition for join, I am getting records in diff rows. I've removed these 2 condition and it worked.
WITH cte AS (
SELECT id, Contact, amount,contactid, ROW_NUMBER()
over (
PARTITION BY id
order by amount desc
) AS RowNo
FROM contacts
where contact is not null
)
select *from id, name,
STUFF ((
SELECT distinct '; ' + isnull(contact,'') FROM cte
WHERE co.id= cte.id
and RowNo <= 5
FOR XML PATH('')),1, 1, '')as contact
from cte where rowno <= 5
You can use conditional aggregation:
id, name, contact,
select id, name,
concat(max(case when seqnum = 1 then contact + ';' end),
max(case when seqnum = 2 then contact + ';' end),
max(case when seqnum = 3 then contact + ';' end),
max(case when seqnum = 4 then contact + ';' end),
max(case when seqnum = 5 then contact + ';' end)
) as contacts
from (select c.*
row_number() over (partition by id order by amount desc) as seqnum
from contacts c
where contact is not null
) c
group by id, name;
If you are running SQL Server 2017 or higher, you can use string_agg(): as most other aggregate functions, it ignores null values by design.
select id, name, string_agg(contact, ',') within group (order by rn) all_contacts
from (
select id, name, contact
row_number() over (partition by id order by amount desc) as rn
from contacts
where contact is not null
) t
where rn <= 5
group by id, name
Note that you don't strictly need a CTE here; you can return the columns you need from the subquery, and use them directly in the outer query.
In earlier versions, one approach using stuff() and for xml path is:
with cte as (
select id, name, contact,
row_number() over (partition by id order by amount desc) as rn
from contacts
where contact is not null
)
select id, name,
stuff(
(
select ', ' + c1.concat
from cte c1
where c1.id = c.id and c1.rn <= 5
order by c1.rn
for xml path (''), type
).value('.', 'varchar(max)'), 1, 2, ''
) all_contacts
from cte
group by id, name
I agree with #GMB. STRING_AGG() is what you need ...
WITH
contacts(Id,nm,ContactId,Contact,Amount) AS (
SELECT 1,'A',1,12323432,555
UNION ALL SELECT 1,'A',2,23432434,349
UNION ALL SELECT 2,'B',3,98867665,297
UNION ALL SELECT 2,'B',4,88867662,142
UNION ALL SELECT 2,'B',5,NULL ,698
)
,
with_filter_val AS (
SELECT
*
, ROW_NUMBER() OVER(PARTITION BY id ORDER BY amount DESC) AS rn
FROM contacts
)
SELECT
id
, nm
, STRING_AGG(CAST(contact AS CHAR(8)),',') AS contact_list
FROM with_filter_val
WHERE rn <=5
GROUP BY
id
, nm
-- out id | nm | contact_list
-- out ----+----+-------------------
-- out 1 | A | 12323432,23432434
-- out 2 | B | 98867665,88867662

Limit MAX() result to one row based on highest value in a particular field

Of course my data set is more complex, but this is essentially what I have:
+--------+--------+-------+
| SEQ_NO | FILTER | VALUE |
+--------+--------+-------+
| 1 | 'A' | 5 |
| 2 | 'A' | 10 |
| 3 | 'A' | 15 |
+--------+--------+-------+
Here is my query:
SELECT MAX(SEQ_NO)
, FILTER
, VALUE
FROM TABLE
GROUP BY FILTER
, VALUE
This returns my entire data set. How can I alter my query so that it only returns the record with the highest SEQ_NO ?
SELECT t1.*
FROM Table AS t1
INNER JOIN
(
SELECT MAX(SEQ_NO) MAXSeq
, FILTER
, VALUE
FROM TABLE
GROUP BY FILTER
, VALUE
) t2 ON t1.SEQ_NO = t2.MAXSeq
AND t1.FILTER = t2.FILTER
AND t1.VALUE = t2.VALUE
Or using row_number:
SELECT *
FROM
(
SELECT *,
row_number() over(partition by FILTER, VALUE
order by SEQ_NO desc) as rn
FROM table
) t
WHERE rn = 1
From Oracle 12C:
SELECT SEQ_NO
, FILTER
, VALUE
FROM TABLE
ORDER BY SEQ_NO DESC
FETCH FIRST 1 ROWS ONLY;
You can use ROWNUM in oracle:
select *
from
( select *
from yourTable
order by SEQ_NO desc ) as t
where ROWNUM = 1;
This should work
SELECT TOP 1 *
FROM TABLE
ORDER BY SEQ_NO DESC
If I understand correctly, you want the top SEQ_NO per filter?
i've created this in SQL Server and converted to Oracle
SELECT a.SEQ_NO,
a.FILTER,
a.VALUE
FROM (
SELECT SEQ_NO,
FILTER,
VALUE,
MAX(SEQ_NO) OVER (PARTITION BY FILTER) m
FROM TABLE
) a
WHERE SEQ_NO = m
Using mysql
SELECT SEQ_NO
, VALUE
, FILTER
FROM TABLE
Order by SEQ_NO DESC LIMIT 1

I want to get minimum of starttime of each id. But I'm not able to achieve it using min function. How to solve it?

Table:
id | starttime | grade
-------------------------
1 | 4PM | A
1 | 5PM | C
2 | 2PM | A
2 | 3PM | B
In output I should get all the ids that have minimum starttime with all the columns.
For the above table output should be like this:
id | starttime | grade
-------------------------
1 | 4PM | A
2 | 2PM | A
Using top with ties and row_number() to get the minimum starttime for each id.
select top (1) with ties *
from t
order by row_number() over (partition by id order by starttime);
rextester demo: http://rextester.com/RJVT1405
returns:
+----+-----------+-------+
| id | starttime | grade |
+----+-----------+-------+
| 1 | 4PM | A |
| 2 | 2PM | A |
+----+-----------+-------+
In SQL Server, use top with ties:
select top (1) with ties t.*
from t
order by starttime;
TOP (1) only returns one row. TOP (1) WITH TIES returns all rows that have the same key value as in the first row specified by the ORDER BY.
You can use group by:
with minTimes (id, startTime) as
(
select id, min(startTime) from myTable
group by id
)
select t1.*
from myTable t1
inner join minTimes t2 on t1.id = t2.id and t1.startTime = t2.startTime
;WITH cte(id,starttime, grade)
AS
(
SELECT 1 , '4PM','A' Union all
SELECT 1 , '5PM','C' Union all
SELECT 2 , '2PM','A' Union all
SELECT 2 , '3PM','B'
)
,Final AS (
SELECT *
,Row_Number() OVER (
PARTITION BY MinStartTime ORDER BY MinStartTime
) Seq
FROM (
SELECT id
,CAST(starttime AS TIME) AS starttime
,MIN(CAST(starttime AS TIME)) OVER (
PARTITION BY ID ORDER BY starttime
) AS MinStartTime
,grade
FROM cte
) Dt
)
SELECT id
,CONVERT(VARCHAR(15), MinStartTime, 100) AS starttime
,grade
FROM final
WHERE Seq = 1
ORDER BY 1

Count of duplicate values by two columns in SQL Server

From this table:
Number Value
1 a
2 b
3 a
2 c
2 b
3 a
2 b
I need to get count of all duplicate rows by Number and Value, i.e. 5.
Thanks.
I think this query is what you want:
SELECT SUM(t.cnt)
FROM
(
SELECT COUNT(*) cnt
FROM table_name
GROUP BY number, value
HAVING COUNT(*) > 1
)t;
Maybe something like this?
select value,number,max(cnt) as Count_distinct from (
select *,row_number () over (partition by value,number order by number) as cnt
from #sample
)t
group by value,number
Output
+---------------------------------+
| Value | Number | Count_Distinct |
| a | 1 | 1 |
| b | 2 | 3 |
| c | 2 | 1 |
| a | 3 | 2 |
+---------------------------------+
Select
count(distinct Number) as Distinct_Numbers,
count(distinct Value) as Distinct_Values
from
Table
This shows how many distinct values are in each column. Does this help?
Give a row number partition by both the columns and order by both the columns. Then count the number of rows where row number greater than 1.
Query
;with cte as(
select [rn] = row_number() over(
partition by [Number], [Value]
order by [Number], [Value]
), *
from [your_table_name]
)
select count(*) from cte
where [rn] > 1;
I think you mean number of unique number - value pairs, you can use:
SELECT count(*)
FROM
(SELECT ROW_NUMBER() OVER (PARTITION BY number, value ORDER BY (select 1)) from mytable rnk) i
where i.rnk = 1
May be this query may help you
select * from [dbo].[Sample_table1]
;WITH
DupContactRecords(number,value,DupsCount)
AS
(
SELECT number,value, COUNT() AS TotalCount FROM [Sample_table1] GROUP BY number,value HAVING COUNT() > 1
)
--to get the duplicats
/*select * from DupContactRecords*/
SELECT sum(DupsCount) FROM DupContactRecords