Can we use rank function over random and another db field? - sql

Code :
select name,rank() over (order by name asc) as rank,
ABS(CAST(CAST(NEWID() AS VARBINARY) AS INT)) AS [RandomNumber]
from Student
I would like to combine random number and name to get rank

I do not know why you want to use a random number with rank() instead of just using row_number(), but it goes something like this:
rextester: http://rextester.com/OLKI98516
create table student(
id int not null identity(1,1) primary key
, name varchar(255) not null
);
insert into student values
('Santosh'),('Kumar'),('Reddy'),('Badugula'),('SqlZim')
,('Emma'),('Xandra'),('Naida'),('Daria'),('Colby'),('Yetta')
,('Zena'),('Deacon'),('Francis'),('Lilah'),('Risa'),('Lee')
,('Vanna'),('Molly'),('Destiny'),('Tallulah'),('Meghan')
,('Deacon'),('Francis'),('Daria'),('Colby');
select
name
, RandomNumber = abs(cast(cast(newid() as varbinary) as int))
, Name_w_RandomNumber = concat(name, '_', abs(cast(cast(newid() as varbinary) as int)))
, rank = rank() over (order by name asc)
, row_number = row_number() over (order by name asc)
, rank_w_Rand = rank() over (order by name,abs(cast(cast(newid() as varbinary) as int)) asc)
from student
results:
+----------+--------------+---------------------+------+------------+-------------+
| name | RandomNumber | Name_w_RandomNumber | rank | row_number | rank_w_Rand |
+----------+--------------+---------------------+------+------------+-------------+
| Badugula | 1105357025 | Badugula_1036749632 | 1 | 1 | 1 |
| Colby | 1125329440 | Colby_1442709274 | 2 | 2 | 2 |
| Colby | 1891932149 | Colby_1045919975 | 2 | 3 | 3 |
| Daria | 1494409363 | Daria_112566484 | 4 | 4 | 4 |
| Daria | 666341314 | Daria_262264162 | 4 | 5 | 5 |
| Deacon | 1530588472 | Deacon_1783529467 | 6 | 6 | 6 |
| Deacon | 350443065 | Deacon_1150932866 | 6 | 7 | 7 |
| Destiny | 2007923301 | Destiny_793747374 | 8 | 8 | 8 |
| Emma | 435476101 | Emma_659930976 | 9 | 9 | 9 |
| Francis | 1638790395 | Francis_2132056162 | 10 | 10 | 10 |
| Francis | 793873129 | Francis_756254272 | 10 | 11 | 11 |
| Kumar | 20071275 | Kumar_2007808448 | 12 | 12 | 12 |
| Lee | 2069120264 | Lee_837143565 | 13 | 13 | 13 |
| Lilah | 1319087807 | Lilah_605243166 | 14 | 14 | 14 |
| Meghan | 487733175 | Meghan_1884481541 | 15 | 15 | 15 |
| Molly | 2086860257 | Molly_1914281986 | 16 | 16 | 16 |
| Naida | 169335218 | Naida_719205571 | 17 | 17 | 17 |
| Reddy | 528578158 | Reddy_1297094295 | 18 | 18 | 18 |
| Risa | 1826403411 | Risa_1530611023 | 19 | 19 | 19 |
| Santosh | 723134579 | Santosh_487617337 | 20 | 20 | 20 |
| SqlZim | 937324776 | SqlZim_738072767 | 21 | 21 | 21 |
| Tallulah | 521881065 | Tallulah_1717653898 | 22 | 22 | 22 |
| Vanna | 1508284361 | Vanna_1620612208 | 23 | 23 | 23 |
| Xandra | 532483290 | Xandra_493053714 | 24 | 24 | 24 |
| Yetta | 1735945301 | Yetta_1548495144 | 25 | 25 | 25 |
| Zena | 311372084 | Zena_1429570716 | 26 | 26 | 26 |
+----------+--------------+---------------------+------+------------+-------------+

Here is the query I was referring to in my comment, not pretty but functional. You asked so here it is, if I am correct I still would never use a random value myself as each time you run it you will get different results.
select name,
rank() over (order by name,
ABS(CAST(CAST(NEWID() AS VARBINARY) AS INT))) asc) as rank
from Student
EDIT: With cte to show random number, NEWID() is guaranteed unique but not sure if it will still be when using ABS, you will need to look into that.
with cteQry As
( select name, ABS(CAST(CAST(NEWID() AS VARBINARY) AS INT))) NewIdVal
from Student
)
select name, NewIdVal,
rank() over (order by name, NewIdVal asc) as rank
from cteQry

Related

SQL Server : sort ranking list

I have a table with 3 columns ordernum, username, and amount. I want to select its rows and show an additional column expected.
The rule for calculating the expected column is as follows:
These rows same OrderNum value will be ranking again based on amount column (Desc order). I don't know how I can describe, but the expected result is shown below :(
I tried with RANK() and ROW_NUMBER(), but have not been able to properly apply above algorithm.
This is my table declaration:
CREATE TABLE data
(
ordernum INT,
username NVARCHAR(30),
amount MONEY
);
This is my table content:
+----------+----------+------------+
| ORDERNUM | USERNAME | AMOUNT |
+----------+----------+------------+
| 1 | test01 | 18382.5079 |
| 1 | test02 | 10476.0000 |
| 1 | test03 | 8128.0000 |
| 1 | test04 | 6680.0000 |
| 1 | test05 | 5388.9673 |
| 1 | test06 | 5356.0000 |
| 12 | test07 | 2806.0000 |
| 12 | test08 | 2806.0000 |
| 12 | test09 | 2806.0000 |
| 14 | test10 | 2530.0000 |
| 15 | test11 | 2330.0000 |
| 16 | test12 | 2183.0000 |
| 16 | test13 | 2182.0000 |
| 17 | test14 | 2000.0000 |
| 18 | test15 | 1621.0000 |
+----------+----------+------------+
And this is my expected result:
+----------+----------+------------+----------+
| ORDERNUM | USERNAME | AMOUNT | EXPECTED |
+----------+----------+------------+----------+
| 1 | test01 | 18382.5079 | 1 |
| 1 | test02 | 10476.0000 | 2 |
| 1 | test03 | 8128.0000 | 3 |
| 1 | test04 | 6680.0000 | 4 |
| 1 | test05 | 5388.9673 | 5 |
| 1 | test06 | 5356.0000 | 6 |
| 12 | test07 | 2806.0000 | 12 |
| 12 | test08 | 2806.0000 | 12 |
| 12 | test09 | 2806.0000 | 12 |
| 14 | test10 | 2530.0000 | 15 |
| 15 | test11 | 2330.0000 | 16 |
| 16 | test12 | 2183.0000 | 17 |
| 16 | test13 | 2182.0000 | 18 |
| 17 | test14 | 2000.0000 | 19 |
| 18 | test15 | 1621.0000 | 20 |
+----------+----------+------------+----------+
Here is a fiddle for the problem: https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=4014cb469a9ec8f57ded5a5e0e60adaf
This should work.
select
OrderNum,
Username,
Amount,
RANK() over (order by OrderNum) as Expected
from yourTable

How to get values of rows and columns

I have two tables.
Student Table
Property Table
Result Table
How can I get the value of Student Table and the property ID of the column fron the Property table and merge that into the Result table?
Any advice would be helpful.
Update #1:
I tried using Christian Moen 's suggestion, this is what i get.
You need to UNPIVOT the Student's columns first, to get the columns (properties names) in one column as rows. Then join with the Property table based on the property name like this:
WITH UnPivoted
AS
(
SELECT ID, value,col
FROM
(
SELECT ID,
CAST(Name AS NVARCHAR(50)) AS Name,
CAST(Class AS NVARCHAR(50)) AS Class,
CAST(ENG AS NVARCHAR(50)) AS ENG,
CAST(TAM AS NVARCHAR(50)) AS TAM,
CAST(HIN AS NVARCHAR(50)) AS HIN,
CAST(MAT AS NVARCHAR(50)) AS MAT,
CAST(PHY AS NVARCHAR(50)) AS PHY
FROM Student
) AS s
UNPIVOT
(value FOR col IN
([Name], [class], [ENG], [TAM], [HIN], [MAT], [PHY])
)AS unpvt
)
SELECT
ROW_NUMBER() OVER(ORDER BY u.ID,PropertyID) AS ID,
p.PropertyID,
u.Value,
u.ID AS StudID
FROM Property AS p
INNER JOIN UnPivoted AS u ON p.PropertyName = u.col;
For the first ID, I used the ranking function ROW_NUMBER() to generate this sequence id.
This will give the exact results that you are looking for.
Results:
| ID | PropertyID | Value | StudID |
|----|------------|--------|--------|
| 1 | 1 | Jack | 1 |
| 2 | 2 | 10 | 1 |
| 3 | 3 | 89 | 1 |
| 4 | 4 | 88 | 1 |
| 5 | 5 | 45 | 1 |
| 6 | 6 | 100 | 1 |
| 7 | 7 | 98 | 1 |
| 8 | 1 | Jill | 2 |
| 9 | 2 | 10 | 2 |
| 10 | 3 | 89 | 2 |
| 11 | 4 | 99 | 2 |
| 12 | 5 | 100 | 2 |
| 13 | 6 | 78 | 2 |
| 14 | 7 | 91 | 2 |
| 15 | 1 | Trevor | 3 |
| 16 | 2 | 12 | 3 |
| 17 | 3 | 100 | 3 |
| 18 | 4 | 50 | 3 |
| 19 | 5 | 49 | 3 |
| 20 | 6 | 94 | 3 |
| 21 | 7 | 100 | 3 |
| 22 | 1 | Jim | 4 |
| 23 | 2 | 8 | 4 |
| 24 | 3 | 100 | 4 |
| 25 | 4 | 91 | 4 |
| 26 | 5 | 92 | 4 |
| 27 | 6 | 100 | 4 |
| 28 | 7 | 100 | 4 |
Other option is to use of apply if you don't want to go unpivot way
select row_number() over (order by (select 1)) ID, p.PropertyID [PropID], a.Value, a.StuID
from Student s
cross apply
(
values (s.ID, 'Name', s.Name),
(s.ID, 'Class', cast(s.Class as varchar)),
(s.ID, 'ENG', cast(s.ENG as varchar)),
(s.ID, 'TAM', cast(s.TAM as varchar)),
(s.ID, 'HIN', cast(s.HIN as varchar)),
(s.ID, 'MAT', cast(s.MAT as varchar)),
(s.ID, 'PHY', cast(s.PHY as varchar))
) as a(StuID, Property, Value)
join Property p on p.PropertyName = a.Property

Group selection by value of some fields

I have a following selection:
| EmployeeId | DepartmentName | Salary |
----------------------------------------
| 1 | A | 10 |
| 2 | A | 10 |
| 3 | A | 15 |
| 4 | A | 20 |
| 5 | A | 20 |
| 6 | B | 15 |
| 7 | B | 25 |
| 8 | B | 25 |
I want to group all rows by salary in each department and add this number of group to the selection.
Example of the result selection:
| EmployeeId | DepartmentName | Salary | Group |
------------------------------------------------
| 1 | A | 10 | 1 |
| 2 | A | 10 | 1 |
| 3 | A | 15 | 2 |
| 4 | A | 20 | 3 |
| 5 | A | 20 | 3 |
| 6 | B | 15 | 1 |
| 7 | B | 25 | 2 |
| 8 | B | 25 | 2 |
SELECT *
, DENSE_RANK() OVER (PARTITION BY DepartmentName
ORDER BY Salary) AS [Group]
FROM TableName

Grouping / Ordering confusion

Hopefully what I have here is a simple question and explained to you in the correct manner.
I have the following Query:
--DECLARE DATES
DECLARE #Date datetime
DECLARE #DaysInMonth INT
DECLARE #i INT
--GIVE VALUES
SET #Date = Getdate()
SELECT #DaysInMonth = datepart(dd,dateadd(dd,-1,dateadd(mm,1,cast(cast(year(#Date) as varchar)+'-'+cast(month(#Date) as varchar)+'-01' as datetime))))
SET #i = 1
--MAKE TEMP TABLE
CREATE TABLE #TempDays
(
[days] VARCHAR(50)
)
WHILE #i <= #DaysInMonth
BEGIN
INSERT INTO #TempDays
VALUES(#i)
SET #i = #i + 1
END
SELECT #TempDays.days, DATEPART(dd, a.ActualDate) ActualDate, a.ActualAmount, (SELECT SUM(b.ActualAmount)
FROM UnpaidManagement..Actual b
WHERE b.ID <= a.ID) RunningTotal
FROM UnpaidManagement..Actual a
RIGHT JOIN #TempDays on a.ID = #TempDays.days
DROP TABLE #TempDays
Which produces the following output:
+------+------------+--------------+--------------+
| days | ActualDate | ActualAmount | RunningTotal |
+------+------------+--------------+--------------+
| 1 | 1 | 438706 | R 438 706 |
| 2 | 2 | 16239 | R 454 945 |
| 3 | 3 | 1611264 | R 2 066 209 |
| 4 | 4 | 1157777 | R 3 223 986 |
| 5 | 5 | 470662 | R 3 694 648 |
| 6 | 6 | 288628 | 3983276 |
| 7 | 7 | 245897 | 4229173 |
| 8 | 8 | 5235 | 4234408 |
| 9 | 10 | 375630 | 4610038 |
| 10 | 11 | 95610 | 4705648 |
| 11 | 12 | 87285 | 4792933 |
| 12 | 13 | 73399 | 4866332 |
| 13 | 14 | 59516 | 4925848 |
| 14 | 15 | 918915 | 5844763 |
| 15 | 17 | 1957285 | 7802048 |
| 16 | 18 | 489964 | 8292012 |
| 17 | 19 | 272304 | 8564316 |
| 18 | 20 | 378601 | 8942917 |
| 19 | 22 | 92374 | 9035291 |
| 20 | 23 | 198 | 9035489 |
| 21 | 24 | 1500820 | 10536309 |
| 22 | 25 | 2631057 | 13167366 |
| 23 | 26 | 6466505 | 19633871 |
| 24 | 27 | 3757350 | 23391221 |
| 25 | 28 | 3487466 | 26878687 |
| 26 | 29 | 160197 | 27038884 |
| 27 | 30 | 14000 | 27052884 |
| 28 | NULL | NULL | NULL |
| 29 | NULL | NULL | NULL |
| 30 | NULL | NULL | NULL |
| 31 | NULL | NULL | NULL |
+------+------------+--------------+--------------+
If you look closely at the table above, the "ActualDate" column is missing a few values, EG: 9, 16, etc.
And because of this, the rows are being pushed up instead of being grouped with their correct number? How would I accomplish a group by / anything to keep them in their correct row?
DESIRED OUTPUT:
+------+------------+--------------+--------------+
| days | ActualDate | ActualAmount | RunningTotal |
+------+------------+--------------+--------------+
| 1 | 1 | 438706 | R 438 706 |
| 2 | 2 | 16239 | R 454 945 |
| 3 | 3 | 1611264 | R 2 066 209 |
| 4 | 4 | 1157777 | R 3 223 986 |
| 5 | 5 | 470662 | R 3 694 648 |
| 6 | 6 | 288628 | 3983276 |
| 7 | 7 | 245897 | 4229173 |
| 8 | 8 | 5235 | 4234408 |
| 9 | NULL | NULL | NULL |
| 10 | 10 | 375630 | 4610038 |
| 11 | 11 | 95610 | 4705648 |
| 12 | 12 | 87285 | 4792933 |
| 13 | 13 | 73399 | 4866332 |
| 14 | 14 | 59516 | 4925848 |
| 15 | 15 | 918915 | 5844763 |
| 16 | NULL | NULL | NULL |
| 17 | 17 | 1957285 | 7802048 |
| 18 | 18 | 489964 | 8292012 |
| 19 | 19 | 272304 | 8564316 |
| 20 | 20 | 378601 | 8942917 |
| 21 | NULL | NULL | NULL |
| 22 | 22 | 92374 | 9035291 |
| 23 | 23 | 198 | 9035489 |
| 24 | 24 | 1500820 | 10536309 |
| 25 | 25 | 2631057 | 13167366 |
| 26 | 26 | 6466505 | 19633871 |
| 27 | 27 | 3757350 | 23391221 |
| 28 | 28 | 3487466 | 26878687 |
| 29 | 29 | 160197 | 27038884 |
| 30 | 30 | 14000 | 27052884 |
| 31 | NULL | NULL | NULL |
+------+------------+--------------+--------------+
I know this is a long one to read, but please let me know if I have explained this clearly enough. I have been trying to group by this whole morning, but I keep getting errors.
SELECT #TempDays.days, DATEPART(dd, a.ActualDate) ActualDate, a.ActualAmount, (SELECT SUM(b.ActualAmount)
FROM UnpaidManagement..Actual b
WHERE b.ID <= a.ID) RunningTotal
FROM UnpaidManagement..Actual a
RIGHT JOIN #TempDays on DATEPART(dd, a.ActualDate) = #TempDays.days
If you select the temp table as first table in the select and join to UnpaidManagement..Actual you have the days in correct row and order:
SELECT t.days
,DATEPART(dd, a.ActualDate) ActualDate
,a.ActualAmount
,(
SELECT SUM(b.ActualAmount)
FROM UnpaidManagement..Actual b
WHERE b.ID <= a.ID
) RunningTotal
FROM #TempDays AS t
INNER JOIN UnpaidManagement..Actual AS a ON a.IDENTITYCOL = t.days
ORDER BY t.days
After doing that, cou can add CASE WHEN to generate content for the NULL cells.

Partition by fixed number of records

I like to know how I can partition a window by a fixed number of records.
Example (http://sqlfiddle.com/#!1/7df86).
CREATE TABLE Games
(
id serial primary key,
game_no integer not null,
points integer,
constraint game_no unique (game_no)
);
INSERT INTO Games (game_no, points)
VALUES (3123, 5), (3126, 5), (3135, 8), (3128, null), (3130, 1), (3121, 11),
(3132, 0), (3133, 4), (3110, 7), (3112, null), (3113, 12), (3125, 3),(3134, 8);
I want the sum of the points of three games combined, starting with the highest game number, descending ordered by the game number. Like this.
| GAME_NO | POINTS | SUM_THREE |
|---------|--------|-----------|
| 3135 | 8 | 20 |
| 3134 | 8 | 20 |
| 3133 | 4 | 20 |
| 3132 | 0 | 1 |
| 3130 | 1 | 1 |
| 3128 | (null) | 1 |
| 3126 | 5 | 13 |
| 3125 | 3 | 13 |
| 3123 | 5 | 13 |
| 3121 | 11 | 23 |
| 3113 | 12 | 23 |
| 3112 | (null) | 23 |
| 3110 | 7 | 7 |
How to accomplish this with a window function without using a subquery? I also can't use for example a with statement. It has to be one single query because of the external parser that will execute it (and I have no control over). It seems so simple and I'm breaking my head over it the last couple of days:)
You can use row_number function divided by 3 to assign unique number to each group of 3 consecutive rows. Then use sum as analytical function on each group.
SQL Fiddle
with x(game_no, points, grp) as (
select game_no, points,
ceil(cast(row_number() over (order by game_no desc) as decimal)/ 3)
from games
)
select game_no, points,
sum(points) over (partition by grp)
from x
order by game_no desc;
You can use inline view instead of with construct.
select game_no, points,
sum(points) over (partition by grp)
from (
select game_no, points,
ceil(cast(row_number() over
(order by game_no desc) as decimal)/ 3) as grp
from games
) as x
order by game_no desc;
Results:
| GAME_NO | POINTS | SUM |
|---------|--------|-----|
| 3135 | 8 | 20 |
| 3134 | 8 | 20 |
| 3133 | 4 | 20 |
| 3132 | 0 | 1 |
| 3130 | 1 | 1 |
| 3128 | (null) | 1 |
| 3126 | 5 | 13 |
| 3125 | 3 | 13 |
| 3123 | 5 | 13 |
| 3121 | 11 | 23 |
| 3113 | 12 | 23 |
| 3112 | (null) | 23 |
| 3110 | 7 | 7 |