Select N Rows With Mixed Values - sql

I have a table with columns like
insertTimeStamp, port, data
1 , 20 , 'aaa'
2 , 20 , 'aba'
3 , 20 , '3aa'
4 , 20 , 'aab'
2 , 21 , 'aza'
5 , 21 , 'aha'
8 , 21 , 'aaa'
15 , 22 , '2aa'
Now I need N Rows (Say 4) from that table, ordered asc by insertTimeStamp.
But if possible, I want to get them from different ports.
So the result should be:
1 , 20 , 'aaa'
2 , 20 , 'aba'
2 , 21 , 'aza'
15 , 22 , '2aa'
If there are not enough different values in port I would like select the remaining ones with the lowest insertTimeStamp.

SQL Fiddle Demo
As you can see I create a group_id so group_id = 1 will be the smaller TimeStamp for each port
The second field is time_id so in the ORDER BY after I select all the 1 bring all the 2,3,4 for any port.
SELECT *
FROM (
SELECT *,
row_number() over (partition by "port" order by "insertTimeStamp") group_id,
row_number() over (order by "insertTimeStamp") time_id
FROM Table1 T
) as T
ORDER BY CASE
WHEN group_id = 1 THEN group_id
ELSE time_id
END
LIMIT 4
OUTPUT
| insertTimeStamp | port | data | group_id | time_id |
|-----------------|------|------|----------|---------|
| 1 | 20 | aaa | 1 | 1 |
| 2 | 21 | aza | 1 | 3 |
| 15 | 22 | 2aa | 1 | 8 |
| 2 | 20 | aba | 2 | 2 |

Use row_number():
select *
from (
select insertTimeStamp, port, data
from (
select *, row_number() over (partition by port order by insertTimeStamp) rn
from a_table
) alias
order by rn, insertTimeStamp
limit 4
) alias
order by 1, 2;
inserttimestamp | port | data
-----------------+------+------
1 | 20 | aaa
2 | 20 | aba
2 | 21 | aza
15 | 22 | 2aa
(4 rows)
SqlFiddle

Related

Get some values from the table by selecting

I have a table:
| id | Number |Address
| -----| ------------|-----------
| 1 | 0 | NULL
| 1 | 1 | NULL
| 1 | 2 | 50
| 1 | 3 | NULL
| 2 | 0 | 10
| 3 | 1 | 30
| 3 | 2 | 20
| 3 | 3 | 20
| 4 | 0 | 75
| 4 | 1 | 22
| 4 | 2 | 30
| 5 | 0 | NULL
I need to get: the NUMBER of the last ADDRESS change for each ID.
I wrote this select:
select dh.id, dh.number from table dh where dh =
(select max(min(t.history)) from table t where t.id = dh.id group by t.address)
But this select not correctly handling the case when the address first changed, and then changed to the previous value. For example id=1: group by return:
| Number |
| -------- |
| NULL |
| 50 |
I have been thinking about this select for several days, and I will be happy to receive any help.
You can do this using row_number() -- twice:
select t.id, min(number)
from (select t.*,
row_number() over (partition by id order by number desc) as seqnum1,
row_number() over (partition by id, address order by number desc) as seqnum2
from t
) t
where seqnum1 = seqnum2
group by id;
What this does is enumerate the rows by number in descending order:
Once per id.
Once per id and address.
These values are the same only when the value is 1, which is the most recent address in the data. Then aggregation pulls back the earliest row in this group.
I answered my question myself, if anyone needs it, my solution:
select * from table dh1 where dh1.number = (
select max(x.number)
from (
select
dh2.id, dh2.number, dh2.address, lag(dh2.address) over(order by dh2.number asc) as prev
from table dh2 where dh1.id=dh2.id
) x
where NVL(x.address, 0) <> NVL(x.prev, 0)
);

RANK, ROW_NUMBER on T-SQL

I have rows like this in SQL Server 2014:
id | fld1
---+-----
1 | 100
2 | 100
3 | 80
4 | 102
5 | 100
6 | 80
7 | 102
I would need a partition that without changing order would return:
NewFld | id | fld1
-------+----+------
1 | 1 | 100
1 | 2 | 100
2 | 3 | 80
3 | 4 | 102
1 | 5 | 100
2 | 6 | 80
3 | 7 | 102
Newfld should return the same value based on fld1 without changing ordering given by id.
I tried with ROW_NUMBER, RANK, DENSE_RANK but nothing works for me.
View this fiddle
Use min() over() in a subquery to establish the ordering values needed for the dense_rank().
SELECT id
, Fld1
, DENSE_RANK() OVER (order by fld1_idmin) AS Rank
FROM (
SELECT id
, fld1
, Min (id) over (partition by fld1) fld1_idmin
FROM yourtable
) d
ORDER BY ID
With an index on FLD1 using these window functions needs just a single index scan for this query. See this SQLfiddle
you may use this
with mytab as
(
SELECT *
,(SELECT MIN(ID) FROM yourtable sub where sub.fld1 = yourtable.fld1) as ranks
FROM yourtable
)
SELECT ID ,fld1 , DENSE_RANK()OVER(ORDER BY Ranks)
FROM mytab
ORDER BY ID
view this fiddle

Oracle query for selecting sampling of number values from a table

I have a data field in my Oracle DB Table whose datatype is NUMBER. I have tried a query below using order by.
SELECT Value
FROM Table
ORDER BY value;
I am getting the result as
Value |
------|
1 |
1 |
2 |
2 |
3 |
3 |
4 |
4 |
5 |
5 |
6 |
6 |
Instead I want a result as
Value |
------|
1 |
2 |
3 |
4 |
5 |
6 |
1 |
2 |
3 |
4 |
5 |
6 |
You can use the row_number to evaluate if an occurrence of a value is the first one, the second, and so on; an order by based on this value and then for the value in the table will do the work.
For example:
/* a test case */
with someTable(value) as (
select 1 from dual union all
select 2 from dual union all
select 3 from dual union all
select 4 from dual union all
select 5 from dual union all
select 6 from dual union all
select 1 from dual union all
select 2 from dual union all
select 3 from dual union all
select 4 from dual union all
select 5 from dual union all
select 6 from dual
)
/* the query */
select value
from someTable
order by row_number() over ( partition by value order by null), value
How it works:
select value, row_number() over ( partition by value order by null) rowNumber
from someTable
order by row_number() over ( partition by value order by null), value
gives:
VALUE ROWNUMBER
---------- ----------
1 1
2 1
3 1
4 1
5 1
6 1
1 2
2 2
3 2
4 2
5 2
6 2
Please try this. I'm using ROW_NUMBER() to arrange the values based on their occurrences,
SELECT VALUE
FROM (
SELECT VALUE
, ROW_NUMBER() OVER (PARTITION BY VALUE ORDER BY VALUE ASC) RNK
FROM MY_TABLE
)
ORDER BY RNK
, VALUE;
1 Value | ------| 1 | 1 | 2 | 2 | 3 | 3 | 4 | 4 | 5 | 5 | 6 | 6 |
SELECT ROW_NUMBER() OVER (PARTITION BY VALUE ORDER BY VALUE) AS RN, TABLE .* FROM TABLE
2 Value | ------| 1 | 2 | 3 | 4 | 5 | 6 | 1 | 2 | 3 | 4 | 5 | 6 |
SELECT ROWNUM,TABLE.* FROM TABLE

Conditional Group By in SQL

I have the following table that I want to group by type. When there are multiple rows with the same type (e.g., A & B type), I want to preserve the 'value' from the row with the highest rank (i.e., primary > secondary > tertiary..)
rowid | type | rank | value
1 | A | primary | 1
2 | A | secondary | 2
3 | B | secondary | 3
4 | B | tertiary | 4
5 | C | primary | 5
So the resulting table should look like
rowid | type | rank | value
1 | A | primary | 1
3 | B | secondary | 3
5 | C | primary | 5
Any suggestions will be highly appreciated!
p.s., I'm working in MS SQL Server.
You can use row_number(). Here is a simple'ish method:
select t.*
from (select t.*,
row_number() over (partition by type
order by charindex(rank, 'primary,secondary,tertiary')
) as seqnum
from t
) t
where seqnum = 1;
This uses charindex() as a simple method of ordering the ranks.
try this,
;WITH CTE
AS (
SELECT *
,row_number() OVER (
PARTITION BY [type] ORDER BY value
) rn
FROM #t
)
SELECT *
FROM cte
WHERE rn = 1
Another way of doing is with Row_Number and an Order By specifying your rule with CASE.
Schema:
CREATE TABLE #TAB(rowid INT, [type] VARCHAR(1), rankS VARCHAR(50) , value INT)
INSERT INTO #TAB
SELECT 1 , 'A' , 'primary' , 1
UNION ALL
SELECT 2 , 'A' , 'secondary', 2
UNION ALL
SELECT 3 , 'B' , 'secondary' , 3
UNION ALL
SELECT 4 , 'B' , 'tertiary' , 4
UNION ALL
SELECT 5 , 'C' , 'primary' , 5
Now apply rank rule with Row_Number
SELECT * FROM (
SELECT ROW_NUMBER() OVER(PARTITION BY [type] ORDER BY (CASE rankS
WHEN 'primary' THEN 1
WHEN 'secondary' THEN 2
WHEN 'tertiary' THEN 3 END )) AS SNO, * FROM #TAB
)A
WHERE SNO =1
Result:
+-----+-------+------+-----------+-------+
| SNO | rowid | type | rankS | value |
+-----+-------+------+-----------+-------+
| 1 | 1 | A | primary | 1 |
| 1 | 3 | B | secondary | 3 |
| 1 | 5 | C | primary | 5 |
+-----+-------+------+-----------+-------+

Select a row X times

I have a very specific sql problem.
I have a table given with order positions (each position belongs to one order, but this isn't a problem):
| Article ID | Amount |
|--------------|----------|
| 5 | 3 |
| 12 | 4 |
For the customer, I need an export with every physical item that is ordered, e.g.
| Article ID | Position |
|--------------|------------|
| 5 | 1 |
| 5 | 2 |
| 5 | 3 |
| 12 | 1 |
| 12 | 2 |
| 12 | 3 |
| 12 | 4 |
How can I build my select statement to give me this results? I think there are two key tasks:
1) Select a row X times based on the amount
2) Set the position for each physical article
You can do it like this
SELECT ArticleID, n.n Position
FROM table1 t JOIN
(
SELECT a.N + b.N * 10 + 1 n
FROM
(SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) a
,(SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) b
) n
ON n.n <= t.amount
ORDER BY ArticleID, Position
Note: subquery n generates a sequence of numbers on the fly from 1 to 100. If you do a lot of such queries you may consider to create persisted tally(numbers) table and use it instead.
Here is SQLFiddle demo
or using a recursive CTE
WITH tally AS (
SELECT 1 n
UNION ALL
SELECT n + 1 FROM tally WHERE n < 100
)
SELECT ArticleID, n.n Position
FROM table1 t JOIN tally n
ON n.n <= t.amount
ORDER BY ArticleID, Position
Here is SQLFiddle demo
Output in both cases:
| ARTICLEID | POSITION |
|-----------|----------|
| 5 | 1 |
| 5 | 2 |
| 5 | 3 |
| 12 | 1 |
| 12 | 2 |
| 12 | 3 |
| 12 | 4 |
Query:
SQLFIDDLEExample
SELECT t1.[Article ID],
t2.number
FROM Table1 t1,
master..spt_values t2
WHERE t1.Amount >= t2.number
AND t2.type = 'P'
AND t2.number <= 255
AND t2.number <> 0
Result:
| ARTICLE ID | NUMBER |
|------------|--------|
| 5 | 1 |
| 5 | 2 |
| 5 | 3 |
| 12 | 1 |
| 12 | 2 |
| 12 | 3 |
| 12 | 4 |