how to rank row for same repeating value in sql - sql

Given this data how can give rank for each repeating data. 1 to 5 i want to rank as 1 and next 1 to 5 i want to rank as 2
Data
1
2
3
4
5
1
2
3
4
5
Expecting output
Data | Column
1 1
2 1
3 1
4 1
5 1
1 2
2 2
3 2
4 2
5 2
I was trying to implement using row number but Below is the exact requirement that i have to implement :
Refcol value column
1 refers to time
2 refers to name
3 refers to location
4 refers to Available (1 or 0 or null)
ID | Refcol | Metric
1 1 02/02/2022
1 2 Adam
1 3 Japan
1 4 1
1 1 03/02/2022
1 2 Smith
1 3 England
1 4 0
Now i want to transform above data as shown below
Expected Ouput
ID | time | name | location | Available
1 02/02/2022 Adam Japan 1
1 03/02/2022 Smith England 0

Best you can do with the limited sample data is to create a row number for each time a number appears. Then order by the row number and then the number. If this doesn't work, then show us more real data.
When using ROW_NUMBER, there's no guarantee that the first 1 through 5 group will be ordered correctly. You have to have some other identifier to guarantee the ordering (i.e. time stamp, set number, parent group, etc.).
SQL Fiddle
MS SQL Server 2017 Schema Setup:
CREATE TABLE Numbers (
num int not null
);
INSERT INTO Numbers
VALUES (1),(2),(3),(4),(5),(1),(2),(3),(4),(5)
Query 1:
WITH prelim AS (
SELECT n.num
, ROW_NUMBER() OVER(PARTITION BY n.num ORDER BY n.num ASC) as row_num
FROM Numbers as n
)
SELECT
p.num
, p.row_num
FROM prelim as p
ORDER BY p.row_num, p.num
Results:
| num | row_num |
|-----|---------|
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 4 | 1 |
| 5 | 1 |
| 1 | 2 |
| 2 | 2 |
| 3 | 2 |
| 4 | 2 |
| 5 | 2 |
UPDATE: If you are dead-set on not changing the data structure, then the best you can do is loop through the data and assigning a unique SetID number to each group of 4 rows. There is no guarantee that this will work when you have more than 4 rows in the table since you have no column to guarantee a consistent sort order.
CREATE TABLE attributes (
ID int not null
, RefCol int not null
, Metric nvarchar(50) not null
, SetID int null
);
INSERT INTO attributes (ID, RefCol, Metric)
VALUES
(1,1,'02/02/2022')
,(1,2,'Adam')
,(1,3,'Japan')
,(1,4,'1')
,(1,1,'03/02/2022')
,(1,2,'Smith')
,(1,3,'England')
,(1,4,'0')
;
DECLARE #setID int = 0;
WHILE (EXISTS (SELECT ID FROM attributes WHERE SetID is NULL))
BEGIN
UPDATE TOP (4) attributes
SET SetID = #setID
FROM attributes
WHERE SetID IS NULL
;
SET #setID = #setID + 1;
END
SELECT * FROM attributes;
SELECT DISTINCT
a.SetID
, a.ID
, aTime.Metric as [time]
, aName.Metric as [name]
, aLoc.Metric as [location]
, aAvail.Metric as [Available]
FROM attributes as a
LEFT OUTER JOIN attributes as aTime
ON aTime.SetID = a.SetID
AND aTime.RefCol = 1
LEFT OUTER JOIN attributes as aName
ON aName.SetID = a.SetID
AND aName.RefCol = 2
LEFT OUTER JOIN attributes as aLoc
ON aLoc.SetID = a.SetID
AND aLoc.RefCol = 3
LEFT OUTER JOIN attributes as aAvail
ON aAvail.SetID = a.SetID
AND aAvail.RefCol = 4
;
ID
RefCol
Metric
SetID
1
1
02/02/2022
0
1
2
Adam
0
1
3
Japan
0
1
4
1
0
1
1
03/02/2022
1
1
2
Smith
1
1
3
England
1
1
4
0
1
SetID
ID
time
name
location
Available
0
1
02/02/2022
Adam
Japan
1
1
1
03/02/2022
Smith
England
0
fiddle

You're probably better off asking the question you actually have.
The result you want can be achieved from the data you gave, but it's going to be non-deterministic.
DECLARE #ints TABLE (INT INT)
INSERT INTO #ints (INT) VALUES
(1), (2), (3), (4), (5),
(1), (2), (3), (4), (5)
SELECT INT, ROW_NUMBER() OVER (PARTITION BY INT ORDER BY INT) AS rn
FROM #ints
ORDER BY rn, INT
INT rn
------
1 1
2 1
3 1
4 1
5 1
1 2
2 2
3 2
4 2
5 2

Related

SQL: Add values to STDEVP calculation

I have the following table.
Key | Count | Amount
----| ----- | ------
1 | 2 | 10
1 | 2 | 15
2 | 5 | 1
2 | 5 | 2
2 | 5 | 3
2 | 5 | 50
2 | 5 | 20
3 | 3 | 5
3 | 3 | 4
3 | 3 | 5
Sorry I couldn't figure out who to make the above a table.
I'm running this on SQL Server Management Studio 2012.
I'd like the stdevp return of the amount columns but if the number of records is less than some value 'x' (there will never be more than x records for a given key), then I want to add zeros to account for the remainder.
For example, if 'x' is 6:
for key 1, I need stdevp(10,5,0,0,0,0)
for key 2, I need stdevp(1,2,3,50,20,0)
for key 3, I need stdevp(5,4,5,0,0,0)
I just need to be able to add zeros to the calculation. I could insert records to my table, but that seems rather tedious.
This seems complicated -- padding data for each key. Here is one approach:
with xs as (
select 0 as val, 1 as n
union all
select 0, n + 1
from xs
where xs.n < 6
)
select k.key, stdevp(coalesce(t.amount, 0))
from xs cross join
(select distinct key from t) k left join
(select t.*, row_number() over (partition by key order by key) as seqnum
from t
) t
on t.key = k.key and t.seqnum = xs.n
group by k.key;
The idea is that the cross join generates 6 rows for each key. Then the left join brings in available rows, up to the maximum.

Update existing records based on the order from a different column

I have the following table:
X_ID X_NAME X_TYPE X_SORT_ID
10 BOOK 1 NULL
20 PEN 1 NULL
30 WATCH 2 NULL
5 TENT 3 NULL
What I'm trying to achieve is to populate the X_SORT_ID column with incremented values starting with 1 based on value in X_ID.
So the table would look like this:
X_ID X_NAME X_TYPE X_SORT_ID
10 BOOK 1 2
20 PEN 1 3
30 WATCH 2 4
5 TENT 3 1
I need to update this table only for all existing rows.
The records that will be added in the future will use a sequence that would set the X_SORT_ID field to the next value.
The only query I came up with is not exactly what I need.
UPDATE X_ITEMS
SET X_SORT_ID = (SELECT MAX(X_ID) FROM X_ITEMS) + ROWNUM
WHERE X_SORT_ID IS NULL;
I could use just a rownum, but this would assign value of 4 to the last record with X_ID = 5, which is not what I wanted.
I'd be thankful for any suggestions.
Can use oracle row_number :
update query
update items ot
set X_SORT_ID =
(
select rw from
(
select X_ID, row_number() over ( order by X_ID ) as rw from items
) it
where it.X_ID = ot.X_ID
)
;
result table
+------+--------+--------+-----------+
| X_ID | X_NAME | X_TYPE | X_SORT_ID |
+------+--------+--------+-----------+
| 10 | BOOK | 1 | 2 |
| 20 | PEN | 1 | 3 |
| 30 | WATCH | 2 | 4 |
| 5 | TENT | 3 | 1 |
+------+--------+--------+-----------+
sqlfiddle
Using ROWNUM (a pseudocolumn) instead of ROWNUMBER(an analytic function) as used above.
Read here for difference
X_ID should be defined as primary key.
update Grentley GY
set X_SORT_ID =
(select rno from
(select X_ID,rownum as rno from Grentley GY
order by x_id ) AB
where AB.X_ID= GY.X_ID
) ;
SQL Fiddle
Sample

How to select extra columns while using group by clause?

I have a table which contains data in this format.
productid filterName boolfilter numericfilter
1 X 1 NULL
1 Y NULL 99inch
1 Z 0 NULL
2 Y NULL 55kg
2 Y NULL 45kg
3 K NULL 20
3 M NULL 35
3 N NULL 25
4 X 1 NULL
4 K 1 NULL
I need data in this format.
Need products where only numeric filters are setup but no boolean filters
productid filterName numericfilter
2 Y 55kg
2 Y 45kg
3 K 20
3 M 35
3 N 25
I have written this query,
SELCT productid
FROM tbl_filters
GROUP BY productid
HAVING SUM(CAST(boolfilter AS INT)) IS NULL
I am getting prouctid 2 and 3, but i need the extra columns also as i have mentioned.
When i am using multiple columns in groupby clause i am not getting the required output.
SELECT t.productid, t.filterName, t.numericfilter
FROM Table_Name t
WHERE t.numericfilter IS NOT NULL
AND NOT EXISTS (SELECT 1
FROM TABLE_NAME
WHERE t.productid = productid
AND boolfilter IS NOT NULL)
Working SQL FIDDLE
| PRODUCTID | FILTERNAME | NUMERICFILTER |
|-----------|------------|---------------|
| 2 | Y | 55kg |
| 2 | Y | 45kg |
| 3 | K | 20 |
| 3 | M | 35 |
| 3 | N | 25 |
Use window functions instead:
SELECT productid, filterName, numericfilter
FROM (SELECT f.*,
MAX(boolfilter) OVER (PARTITION BY productid) as maxbf
FROM tbl_filters f
) f
WHERE maxbf is null;
Fiddle DEMO.
This calculates the maximum of boolfilter for each productid. If it is always NULL, then the result is NULL. Note that you don't need a cast() for this.

Count rows in each 'partition' of a table

Disclaimer: I don't mean partition in the window function sense, nor table partitioning; I mean it in the more general sense, i.e. to divide up.
Here's a table:
id | y
----+------------
1 | 1
2 | 1
3 | 1
4 | 2
5 | 2
6 | null
7 | 2
8 | 2
9 | null
10 | null
I'd like to partition by checking equality on y, such that I end up with counts of the number of times each value of y appears contiguously, when sorted on id (i.e. in the order shown).
Here's the output I'm looking for:
y | count
-----+----------
1 | 3
2 | 2
null | 1
2 | 2
null | 2
So reading down the rows in that output we have:
The first partition of three 1's
The first partition of two 2's
The first partition of a null
The second partition of two 2's
The second partition of two nulls
Try:
SELECT y, count(*)
FROM (
SELECT y,
sum( xyz ) OVER (
ORDER BY id
rows between unbounded preceding
and current row
) qwe
FROM (
SELECT *,
case
when y is null and
lag(y) OVER ( ORDER BY id ) is null
then 0
when y = lag(y) OVER ( ORDER BY id )
then 0
else 1 end xyz
FROM table1
) alias
) alias
GROUP BY qwe, y
ORDER BY qwe;
demo: http://sqlfiddle.com/#!15/b1794/12

Stripe the order of a PostgreSQL result set

Let's say I have the following table:
create temp table test (id serial, number integer);
insert into test (number)
values (5), (4), (3), (2), (1), (0);
If I sort by number descending, I get:
select * from test order by number desc;
id | number
---+--------
1 | 5
2 | 4
3 | 3
4 | 2
5 | 1
6 | 0
If I sort by number ascending, I get:
select * from test order by number asc;
6 | 0
5 | 1
4 | 2
3 | 3
2 | 4
1 | 5
How do I stripe the order so that it alternates between ascending and descending per row?
for example:
6 | 0 or 1 | 5
1 | 5 6 | 0
5 | 1 2 | 4
2 | 4 5 | 1
4 | 2 3 | 3
3 | 3 4 | 2
Update
WITH x AS (
SELECT *
, row_number() OVER (ORDER BY number) rn_up
, row_number() OVER (ORDER BY number DESC) rn_down
FROM test
)
SELECT id, number
FROM x
ORDER BY LEAST(rn_up, rn_down), number;
Or:
...
ORDER BY LEAST(rn_up, rn_down), number DESC;
to start with the bigger number.
I had two CTE at first, but one is enough - simpler and faster.
Or like this (similar to the already given answer but slightly shorter :)
WITH x AS (
SELECT *, row_number() OVER (ORDER BY number) rn, count(*) over () as c
FROM test
)
SELECT id, number
FROM x
ORDER BY ABS((c + 1.5) / 2 - rn) DESC;
If the reverse order is needed then it should be
ORDER BY ABS((c + 0.5) / 2 - rn) DESC;