Reward points by looking at the already created fields - sql

I have a table here :
I want to reward a gold and a silver(i.e a value of 1 wherever applicable,else 0 )to top 2 persons by looking at pointsRewarded field.
I already have the first table created.I want a new table with the two new fields i.e the gold and silver fields.
i want the output to be something like this:
Please help me with the query or give me some suggestions on how to proceed.
Thanks a lot.

I think you want to use dense_rank() for this:
select t.*,
(case when rnk = 1 then 1 else 0 end) as gold,
(case when rnk = 2 then 1 else 0 end) as silver
from (select t.*,
dense_rank() over (partition by week order by pointsrewarded) as rnk
from t
) t;
dense_rank() will handle the case when there are ties. In that case, multiple "gold" and "silver" values will be assigned.
I should also note that the subquery is not necessary. You can repeat the dense_rank() in the outer query. I just think it is easier to follow the logic this way.

Make sure to order by pointsrewarded descending so first place is the highest points and not the lowest. My code is longer, but I find easier to read (personal preference).
--create table employee (employeeid int, employeename varchar(50), weeknumber int, pointsRewarded int, Hours int)
--insert into employee values (111, 'person1', 1, 400, 2)
--insert into employee values (112, 'person2', 1, 100, 10)
--insert into employee values (113, 'person3', 1, 200, 10)
--insert into employee values (111, 'person1', 2, 100, 2)
--insert into employee values (112, 'person2', 2, 50, 10)
--insert into employee values (113, 'person3', 2, 200, 10)
--insert into employee values (111, 'person1', 3, 20, 4)
--insert into employee values (112, 'person2', 3, 25, 5)
--insert into employee values (113, 'person3', 3, 100, 6)
;WITH Medals AS
(
SELECT
employeeid
,employeename
,weeknumber
,pointsRewarded
,hours
,ROW_NUMBER() OVER (PARTITION BY weeknumber ORDER BY pointsrewarded DESC) medal
FROM
employee
)
SELECT
employeeid
,employeename
,weeknumber
,pointsRewarded
,hours
,CASE WHEN medal = 1 THEN 1 ELSE 0 END AS gold
,CASE WHEN medal = 2 THEN 1 ELSE 0 END AS silver
FROM
Medals

Related

How to use ROW_NUMBER when grouping records?

I have the following:
DECLARE #items TABLE
(
ItemId int NOT NULL,
[Description] varchar(255) NOT NULL,
Amount money NOT NULL
);
INSERT INTO #items SELECT 1, 'A', 10;
INSERT INTO #items SELECT 2, 'A', 10;
INSERT INTO #items SELECT 3, 'B', 11;
INSERT INTO #items SELECT 4, 'B', 11;
INSERT INTO #items SELECT 5, 'B', 11;
INSERT INTO #items SELECT 6, 'C', 12;
INSERT INTO #items SELECT 7, 'C', 12;
INSERT INTO #items SELECT 8, 'A', 10;
INSERT INTO #items SELECT 9, 'A', 10;
SELECT
ROW_NUMBER() OVER(PARTITION BY b.ItemId ORDER BY b.[Description]),
[Description],
COUNT(ItemId) OVER(PARTITION BY b.ItemId),
SUM(Amount) OVER(PARTITION BY b.ItemId)
FROM #items b
The result should be:
1, A, 4, 40
2, B, 3, 33
3, C, 2, 24
However the items are not being grouped.
So how to I need to use ROW_NUMBER to group records?
Is this what you want?
SELECT ROW_NUMBER() OVER (ORDER BY i.Description),
i.Description,
COUNT(*),
SUM(i.Amount)
FROM #items i
GROUP BY Description
ORDER BY Description;
Here is a rextester.
If you don't want use GROUP BY by itself you may do a subquery with two row_number(), something like this:
select ROW_NUMBER() over(order by t.[Description]), t.Description, t.cnt, t.summ
from (
SELECT
ROW_NUMBER() OVER(PARTITION BY b.[Description] ORDER BY b.[Description] ) rn,
[Description],
COUNT(ItemId) OVER(PARTITION BY b.[Description]) cnt,
SUM(Amount) OVER(PARTITION BY b.[Description]) summ
FROM #items b
) t where rn = 1
And anyway you shouldn't group data by the ItemId - it's a wrong way to achieve your aim

SQL - find all ROUTEMAP_ID in a table which has a foreign key

I'dlike to ask you for help.
I have one table ROUTEMAP_DETAILS with following columns:
ID FLIGHT_LEG ROUTEMAP_ID (this column is a foreign key and is related to the table ROUTEMAP).
The table ROUTEMAP_DETAILS looks like:
(ID, FLIGHT_LEG, ROUTEMAP_ID):
(1, 0, 222224444)
(2, 0, 334843444)
(3, 1, 345436456)
(4, 2, 434355666)
(5, null, 435058395)
The table ROUTEMAP_DETAILS can contain many rows with the same ROUTEMAP_ID:
(ID, FLIGHT_LEG, ROUTEMAP_ID):
(33, 0, 323232223)
(34, 1, 323232223)
(35, 2, 323232223)
My question is: how to find ALL ROUTEMAP_IDs which have FLIGHT_LEG 1 or 2 or 3 or both/all BUT NO 0? For example I'm looking for this case (I'd like to know if this issue exists in my table):
(ID, FLIGHT_LEG, ROUTEMAP_ID):
(34, 1, 323232223)
(35, 2, 323232223)
I've tried to group my select by ROUTEMQP_ID but I didn't cope with this unfortunately.
Thank you very much for your help!
Best regards
Mateusz
You can use a correlated sub-query with NOT EXISTS:
select ID, FLIGHT_LEG, ROUTEMAP_ID
from ROUTEMAP_DETAILS as a
where not exists
(
select * from ROUTEMAP_DETAILS as b
where a.ROUTEMAP_ID = b.ROUTEMAP_ID
and FLIGHT_LEG = 0
)
AND FLIGHT_LEG > 0
SELECT id,
flight_leg,
routemap_id
FROM (SELECT *,
RANK() OVER (PARTITION BY routemap_id ORDER BY id ASC) AS [rank]
FROM routemap_details
) a
WHERE [rank] > 1
Result
id flight_leg routemap_id
34 1 323232223
35 2 323232223
try this:
SELECT *
FROM ROUTEMAP_DETAILS
WHERE FLIGHT_LEG IN (1, 2, 3)
or try in SQL Fiddle

How can I get the next record date from a date and the last record date from a date?

I create table Appointments with this structure:
CREATE TABLE Appointments
(
[Id] bigint,
[Name] varchar(250),
[DateInit] date
);
INSERT INTO Appointments ([Id], [Name], [DateInit])
values
(1000, 'Lorena', '03/06/2016'),
(1000, 'Lorena', '01/06/2016'),
(1000, 'Lorena', '08/06/2016'),
(1000, 'Lorena', '10/06/2016'),
(1000, 'Lorena', '02/06/2016'),
(1000, 'Lorena', '20/06/2016'),
(7000, 'Susan', '04/06/2016'),
(7000, 'Susan', '08/06/2016'),
(7000, 'Susan', '09/06/2016'),
(7000, 'Susan', '01/06/2016');
This is the final result:
I need to get the result for the next day and the day before, for example if today is '03/06/2016' I need to get result for the last appointment inserted in the table from today and the next appointment inserted in the table from today, the result I need is something like this:
Name Last Visit Next Visit
----- ---------- -----------
Lorena 2016-06-02 2016-06-08
Susan 2016-06-01 2016-06-04
How can I get this result?
Thanks
Do a GROUP BY, use case expressions to pick max previous appointment, and min future appointment:
select name,
max(case when DateInit < CONVERT(DATE,GETDATE()) then DateInit end) as LastVisit,
min(case when DateInit > CONVERT(DATE,GETDATE()) then DateInit end) as NextVisit
from Appointments
group by name
I'd do this as joins to the previous and next visit, something like this;
SELECT DISTINCT
a.ID
,a.NAME
,l.LastVisit
,n.NextVisit
FROM Appointments a
LEFT JOIN (
SELECT ID
,MIN(DateInit) NextVisit
FROM Appointments
WHERE DateInit > GETDATE()
GROUP BY ID
) n ON a.ID = n.ID
LEFT JOIN (
SELECT ID
,MAX(DateInit) LastVisit
FROM Appointments
WHERE DateInit < GETDATE()
GROUP BY ID
) l ON a.ID = l.ID
DECLARE #Appointments TABLE
(
[Id] bigint,
[Name] varchar(250),
[DateInit] date
);
INSERT INTO #Appointments ([Id], [Name], [DateInit])
values
(1000, 'Lorena','2016/06/03'),
(1000, 'Lorena','2016/06/01'),
(1000, 'Lorena','2016/06/08'),
(1000, 'Lorena','2016/06/10'),
(1000, 'Lorena','2016/06/02'),
(1000, 'Lorena','2016/06/20'),
(7000, 'Susan', '2016/06/04'),
(7000, 'Susan', '2016/06/08'),
(7000, 'Susan', '2016/06/09'),
(7000, 'Susan', '2016/06/01');
DECLARE #Today DATE = GETDATE();
WITH CTE
AS (
SELECT A.NAME
,ROW_NUMBER() OVER (
PARTITION BY ID ORDER BY ID
) RN
,(
SELECT TOP 1 DateInit
FROM #Appointments B
WHERE B.ID = A.ID
AND DateInit < #TODAY
ORDER BY DateInit DESC
) [Last Visit]
,(
SELECT TOP 1 DateInit
FROM #Appointments B
WHERE B.ID = A.ID
AND DateInit > #TODAY
ORDER BY DateInit
) [Next Visit]
FROM #Appointments A
--GROUP BY ID
)
SELECT C.NAME
,C.[Last Visit]
,C.[Next Visit]
,RN
FROM CTE C
WHERE RN = 1

How to calculate 90th Percentile, SD, Mean for data in SQL

Hi I have a table facility. Which holds a score for each day (Multiple scores can be reported each day and both would be valid)
I need to calculate the 90th percentile, SD, and Mean for score by month.
Facility:
Id Month Date score
1 Jan 1 5
1 Jan 1 5
1 Jan 2 3
1 Jan 3 4
1 Jan 4 4
1 Jan 5 4
1 Feb 1 5
1 Feb 1 5
1 Feb 2 3
1 Feb 3 4
1 Feb 4 4
1 Feb 5 4
Is there any way?
Thanks for your help.
You can use the new suite of analytic functions introduced in SQL Server 2012:
SELECT DISTINCT
[Month],
Mean = AVG(Score) OVER (PARTITION BY [Month]),
StdDev = STDEV(Score) OVER (PARTITION BY [Month]),
P90 = PERCENTILE_CONT(0.9) WITHIN GROUP (ORDER BY Score) OVER (PARTITION BY [Month])
FROM my_table
There are 2 percentile functions: PERCENTILE_CONT for continuous distribution and PERCENTILE_DISC for discrete distribution. Picks one that suits your needs.
Here's the setup...
CREATE TABLE Facility (Id INT NOT NULL, Month nvarchar(3) NOT NULL, Date INT NOT NULL, score INT NOT NULL)
INSERT INTO Facility (Id, Month, Date, score) VALUES (1, 'Jan', 1, 5)
INSERT INTO Facility (Id, Month, Date, score) VALUES (1, 'Jan', 1, 5)
INSERT INTO Facility (Id, Month, Date, score) VALUES (1, 'Jan', 2, 3)
INSERT INTO Facility (Id, Month, Date, score) VALUES (1, 'Jan', 3, 4)
INSERT INTO Facility (Id, Month, Date, score) VALUES (1, 'Jan', 4, 4)
INSERT INTO Facility (Id, Month, Date, score) VALUES (1, 'Jan', 5, 4)
INSERT INTO Facility (Id, Month, Date, score) VALUES (1, 'Feb', 1, 5)
INSERT INTO Facility (Id, Month, Date, score) VALUES (1, 'Feb', 1, 5)
INSERT INTO Facility (Id, Month, Date, score) VALUES (1, 'Feb', 2, 3)
INSERT INTO Facility (Id, Month, Date, score) VALUES (1, 'Feb', 3, 4)
INSERT INTO Facility (Id, Month, Date, score) VALUES (1, 'Feb', 4, 4)
INSERT INTO Facility (Id, Month, Date, score) VALUES (1, 'Feb', 5, 4)
Now, Standard Deviation and Mean are straight forward enough - there are built in aggregate functions for them...
SELECT
[Month],
AVG(CONVERT(real, score)) AS [Mean],
STDEV(score) AS [Standard Deviation]
FROM
Facility
GROUP BY
[Month]
For your 90th percentile, you'll need to invent a function...
CREATE FUNCTION NintythPercentile(#Month nvarchar(3)) RETURNS INT AS
BEGIN
DECLARE #ReturnValue INT
SELECT
#ReturnValue = MIN(DerivedTopTenPercent.score) --AS [90th Percentile]
FROM
(
SELECT TOP 10 PERCENT
score
FROM
Facility
WHERE
[Month] = #Month
ORDER BY
score DESC
) DerivedTopTenPercent
RETURN #ReturnValue
END
With that function in place, your final query will look like this...
SELECT
[Month],
AVG(CONVERT(real, score)) AS [Mean],
STDEV(score) AS [Standard Deviation],
dbo.NintythPercentile([Month]) AS [90th Percentile]
FROM
Facility
GROUP BY
[Month]

sql question about ordering after a sum

i have a products table that contains all the sales as in quantity made at a time .. so the table is :
id | product_department_id | product_id | quantity_sold
i need to list for all the product_department_ids the best 2 selling products . Any ideas how i can do so ?
if you can do it in pl/sql it would be great but sql is ok also !
Thanks !
drop table quantity;
create table quantity (
id number primary key,
product_department_id number,
product_id number,
quantity_sold number,
unique (product_department_id, product_id)
);
insert into quantity values (1, 1, 1, 10);
insert into quantity values (2, 1, 2, 20);
insert into quantity values (3, 1, 3, 30);
insert into quantity values (4, 2, 1, 60);
insert into quantity values (5, 2, 2, 50);
insert into quantity values (6, 2, 3, 40);
select * from (
select quantity_sold, product_id, product_department_id,
row_number() over (partition by product_department_id order by quantity_sold desc) r
from quantity
) where r < 3;
Edit Still not sure about what exactly was asked, but if the combination prodcut/department can have multple entries then it would be:
drop table quantity;
create table quantity (
id number primary key,
product_department_id number,
product_id number,
quantity_sold number
);
insert into quantity values ( 1, 1, 1, 15);
insert into quantity values ( 2, 1, 1, 15);
insert into quantity values ( 3, 1, 1, 15);
insert into quantity values ( 4, 1, 2, 20);
insert into quantity values ( 5, 1, 3, 30);
insert into quantity values (10, 2, 1, 60);
insert into quantity values (11, 2, 2, 50);
insert into quantity values (12, 2, 3, 40);
insert into quantity values (13, 2, 3, 30);
select * from (
select sum(quantity_sold),
product_id, product_department_id,
row_number() over (partition by product_department_id
order by sum(quantity_sold) desc
) r
from quantity
group by product_department_id, product_id
) where r < 3
order by product_department_id, product_id;
If a product can have only one department, you can simply order by:
select product_department_id
from YourTable
where rownum < 3
order by
quantity_sold desc