How to select a dynamic rank value? - sql

I have a table of companies and another of employees and the dates they joined the company. I am able to get the 5 most recent hires for each company as shown here. Now, I'd like to only show rank <= 3 for Company A, rank <= 8 for Company B and an unlimited number for Company C. 3, 8 and -1 are stored as the "max" column in the company table. How do I dynamically select the max in this case?

You basically want:
SELECT * -- choose the columns you want here
FROM (SELECT e.*, c.max,
row_number() over (partition by company order by joined desc) as rank
FROM employees e JOIN
companies c
on e.company = c.pk
) e
WHERE rank < max or max = -1

Related

Using JOIN in SQL with a bound

I'm new to SQL and I have a question about JOINs.
The question goes like this, There are 2 tables, The first table stores data about Patients and there is an attribute in the patient table called Field, which stores the medical field under which the patient was treated. The second table is called Doctors, and here there is an attribute called Specialization, which stores the medical field in which the doctor specializes.
Medical fields i.e Cardiology, Virology, and so on.
There can be more doctors who practice in the same specialization.
If I were to join the tables on the basis of the Doctors.Specialization and Patients.Field and a constraint of that each doctor will be matched with a maximum of 5 patients, Then what would be the query?
SELECT *
FROM Patients
inner join Doctors on Patients.Diagnosis = Doctors.Specialization;
I would solve it like this:
Join the two tables using specialization and diagnosis columns.
Rank doctors and patients by specialization using DENSE_RANK() analytic function
Filter the data. Patients' ranks must be in a range which's:
lower bound (exclusive) is: (doctors' rank - 1) * 5.
If doctor's rank is 1, then it's 0.
If doctor's rank is 2, then it's 5.
upper bound (inclusive) is: doctors' rank * 5.
If doctor's rank is 1, then it's 5.
If doctor's rank is 2, then it's 10.
WITH base AS (
SELECT d.specialization,
d.id AS doctor_id,
d.name AS doctor_name,
p.id AS patient_id,
p.name AS patient_name,
-- Rank doctors by specialization.
DENSE_RANK() OVER (
PARTITION BY d.specialization
ORDER BY d.id
) AS doc_spec_rank,
-- Rank patients by specialization
DENSE_RANK() OVER (
PARTITION BY d.specialization
ORDER BY p.id
) AS patient_spec_rank
FROM doctors d
INNER JOIN patients p
ON d.specialization = p.diagnosis
)
SELECT *
FROM base
WHERE (
(doc_spec_rank - 1) * 5 < patient_spec_rank
AND doc_spec_rank * 5 >= patient_spec_rank
)
ORDER BY specialization, doc_spec_rank, patient_spec_rank
;
Since you didn't provide your rbdms and test data, I took the liberty of creating a sample schema in Oracle 18c.
Here's a fiddle with the schema and the solution: https://dbfiddle.uk/4_kikOO7

count(*) and having not selecting for same date

I have a small doubt on regards how to organize a group_by.
I have this report that lists different we_date for each employee_payroll. one employee payroll can have records on multiple we_dates.
I want to select only when an employee_payroll has more than 4 occurences (disregarding the date column)
I understand that I need to do it either with a having count(employee_payroll) > 4 or a sub-query,
however when I try to do the having it asks me to group by using the date column, and this doesn't return the count per employee_payroll I need. (If I add a count(employee_payroll) I receive 1 in all rows, but I cannot not group by the date field. what am I doing wrong?
want to select only when an employee_payroll has more than 4 occurences (disregarding the date column)
You would use window functions:
select e.*
from (select e.*, count(*) over (partition by employee_payroll) as cnt
from employee e
) e
where cnt >= 4;
Explicit aggregation is probably not the best way to return this result.
This is hard to tell without the actual error you are getting that asks you to group by we_date, but in theory this should be what you have to do:
SELECT employee_payroll
FROM table
GROUP BY employee_payroll
HAVING count(employee_payroll) > 4;
You can first get employees with > 4 occurances. Then , only select those employee rows.
;WITH CTE_EmployeeWithGreaterThanFourOccurance (
SELECT Employee_Payroll, COUNT(*) AS cnt
FROM Employee
GROUP BY Employee_Payroll
HAVING COUNT(*) > 4
)
SELECT *
FROM Employee AS e
INNER JOIN CTE_EmployeeWithGreaterThanFourOccurance AS c
ON c.Employee_Payroll = e.Employee_Payroll

How to returns number of rows incrementally based on column values

I have departments and issues tables. For every department there are approval levels.
So if say HR department has 3 approval levels, i want the drop down to return a new alias column as Y1,Y2,Y3.
Similarly if finance has 2 it should return Y1 and Y2.
Is it possible in sql?
As of now the first alias column is returning say Y3 for HR, but i want that split in rows Y1,Y2,Y3. is it possible via sql?
Generate a sequence from 1 to the maximum approval levels in a CTE.
WITH CTE as (
SELECT LEVEL n
FROM DUAL
CONNECT BY LEVEL <= (select MAX(approval_level) from p_it_Departments )
)
SELECT 'Y'||c.n as approval
,d.approval_level
,d.dept_name
FROM p_it_issues i
INNER JOIN p_it_Departments d ON i.related_dept_id=d.dept_id
INNER JOIN CTE c ON c.n <= d.approval_level
ORDER BY dept_name
You could also add a DISTINCT to the last SELECT to eliminate the duplicates that were present in your original query as well.
Ok, this would not have been mentioned in comment properly but i figured it out so wanted to share.
with cte as(
SELECT
ROW_NUMBER() OVER(partition by d.dept_name ORDER BY d.dept_name ASC ) AS Row#,
d.approval_level, d.dept_name
FROM p_it_issues i, p_it_Departments d where i.related_dept_id=d.dept_id
)
select 'Y'||cte.Row# from cte;
This would print what i wanted to display.

SQL Selecting dates with maximum sale for each department [duplicate]

This question already has answers here:
Fetch the rows which have the Max value for a column for each distinct value of another column
(35 answers)
Oracle SQL query: Retrieve latest values per group based on time [duplicate]
(2 answers)
Get value based on max of a different column grouped by another column [duplicate]
(1 answer)
SQL: getting the max value of one column and the corresponding other columns [duplicate]
(2 answers)
Closed 3 years ago.
I am troubled with writing a tricky query.
I have the following table:
For each department I want to print date with largest profit;
I tried coming up with such a query myself:
Select DISTINCT(Name), Date_sale, MAX(A) as B FROM (SELECT
Departments.Name, SALES.Date_sale, SUM(GOODS.Price * SALES.Quantity)
AS A FROM DEPARTMENTS, GOODS, SALES
WHERE DEPARTMENTS.Dept_id = GOODS.Dept_id AND GOODS.Good_id =
SALES.Good_id GROUP BY DEPARTMENTs.Name, SALES.Date_sale)
GROUP BY Name, Date_sale;
But the problem it that departments are printed several times because I groupped by both name and date.
How should I fix it?
You can try below way-
with cte as
(
SELECT
Departments.Name, SALES.Date_sale, SUM(GOODS.Price * SALES.Quantity)
AS profit FROM DEPARTMENTS inner join GOODS on DEPARTMENTS.Dept_id = GOODS.Dept_id
inner join SALES on GOODS.Good_id = SALES.Good_id
GROUP BY DEPARTMENTs.Name, SALES.Date_sale
)A
select * from cte a
where profit =
(select max(profit) from cte b on a.department=b.department)
OR you can use row_number()
select * from
(
select *, row_number() over(partition by department oder by profit desc) as rn
from cte
)A where rn=1
You can write it using ROW_NUMBER which will give a number to each date's total count grouped by the department as following and then you can take the highest sale date using rn = 1
SELECT NAME, DATE_SALE, A
FROM
(
SELECT
DEPARTMENTS.NAME, SALES.DATE_SALE,
ROW_NUMBER() OVER(
PARTITION BY DEPARTMENTS.NAME
ORDER BY SUM(GOODS.PRICE * SALES.QUANTITY) DESC NULLS LAST
) AS RN,
SUM(GOODS.PRICE * SALES.QUANTITY) AS A
FROM DEPARTMENTS
JOIN GOODS ON ( DEPARTMENTS.DEPT_ID = GOODS.DEPT_ID )
JOIN SALES ON ( GOODS.GOOD_ID = SALES.GOOD_ID )
GROUP BY DEPARTMENTS.NAME,
SALES.DATE_SALE
)
WHERE RN = 1;
Important, Use the standard ANSI-joins.
Cheers!!
i would use join-s here as it is needed to pull info from 2 tables linked via the third table.
Something like this (but I have not tested this query, just suggesting an approach):
Select department.name as dept, MAX(sales.quantity) as max_sales, sales.date_sale
from goods
Left outer join departments on departments.dept_id = goods.dept_id
Left outer join sales on sales.good_id = goods.good_id
Group by dept

TSQL - Sum of Top 3 records of multiple teams

I am trying to generate a TSQL query that will take the top 3 scores (out of about 50) for a group of teams, sum the total of just those 3 scores and give me a result set that has just the name of the team, and that total score ordered by the score descending. I'm pretty sure it is a nested query - but for the life of me can't get it to work!
Here are the specifics, there is only 1 table involved....
table = comp_lineup (this table holds a separate record for each athlete in a match)
* athlete
* team
* score
There are many athletes to a match - each one belongs to a team.
Example:
id athlete team score<br>
1 1 1 24<br>
2 2 1 23<br>
3 3 2 21<br>
4 4 2 25<br>
5 5 1 20<br>
Thank You!
It is indeed a subquery, which I often put in a CTE instead just for clarity. The trick is the use of the rank() function.
;with RankedScores as (
select
id,
athlete,
team,
score,
rank() over (partition by team order by score desc) ScoreRank
from
#scores
)
select
Team,
sum(Score) TotalScore
from
RankedScores
where
ScoreRank <= 3
group by
team
order by
TotalScore desc
To get the top n value for every group of data a query template is
Select group_value, sum(value) total_value
From mytable ext
Where id in (Select top *n* id
From mytable sub
Where ext.group_value = sub.group_value
Order By value desc)
Group By group_value
The subquery retrieve only the ID of the valid data for the current group_value, the connection between the two dataset is the Where ext.group_value = sub.group_value part, the WHERE in the main query is used to mask every other ID, like a cursor.
For the specific question the template became
Select team, sum(score) total_score
From mytable ext
Where id in (Select top 3 id
From mytable sub
Where ext.team = sub.team
Order By score desc)
Group By team
Order By sum(score) Desc
with the added Order By in the main query for the descending total score