sql- Number of actual records added - sql

I am trying to generate a report based on counselling and actual to see how many counseling were converted to actual.
I have table which has Patient, Booking Category, Doctor, Booking.
SQL Fiddle
Following are the business rules
Fetch all the bookings made by patients if they made booking after counseling
The user can take counseling booking first and book one or more bookings that falls under the same counselling group. Example: They can do a first booking Counseling Dental then can book anything mapped to that group. i. e Dentail surgery and Teeth repair. These information maintained in CounselingXActualBookingCategoryMap table
If patient book more than one actual booking consider only one, which is not cancelled
If counselling is cancelled dont consider.
If actual is cancelled dont consider.
Get a total number of counseling bookings(it need not to be a actual booking doctor) and actual bookings for each doctor. Any one can be a counseling doctor. But report we consider only the doctor who did actual
Here is the SQL i tried. But its taking all records instead of one actual for that group.
SELECT P.Name As Patient, SurgeryB.BookingId, SurgeryB.BookingDate, C.CategoryName,
d.name as doctor
FROM BOOKING CounsB
INNER JOIN BOOKING SurgeryB
ON CounsB.PatientId = SurgeryB.PatientId AND ISNULL(SurgeryB.IsCancelled,0)=0
INNER JOIN CounselingXActualBookingCategoryMap MAP
ON MAP.CounselingCategoryId = CounsB.CategoryId
AND MAP.ActualBookingCategoryId = SurgeryB.CategoryId
AND SurgeryB.BookingDate > CounsB.BookingDate
INNER JOIN PATIENT P ON P.PATIENTID = SurgeryB.PATIENTID
inner join category c on c.categoryid= SurgeryB.CategoryId
inner join doctor d on SurgeryB.doctorid = d.doctorid
WHERE ISNULL(CounsB.IsCancelled,0)=0
order by p.patientid
Issue:
I do not want to see a record that Alex made booking for Teeth repair, since he already did actual for same group Dental and category Dental Surgery
I want to get counseling and actual made bookings. Example 100 counseling made and only 70 actual booked like that for each counseling category.

I have added to your code as below:
SELECT P.Name As Patient, SurgeryB.BookingId, SurgeryB.BookingDate, C.CategoryName, map.GroupId, map.CounselingCategoryId, map.ActualBookingCategoryId,
d.name as doctor
into #temp
FROM BOOKING CounsB
INNER JOIN BOOKING SurgeryB
ON CounsB.PatientId = SurgeryB.PatientId AND ISNULL(SurgeryB.IsCancelled,0)=0
INNER JOIN CounselingXActualBookingCategoryMap MAP
ON MAP.CounselingCategoryId = CounsB.CategoryId
AND MAP.ActualBookingCategoryId = SurgeryB.CategoryId
AND SurgeryB.BookingDate > CounsB.BookingDate
INNER JOIN PATIENT P ON P.PATIENTID = SurgeryB.PATIENTID
inner join category c on c.categoryid= SurgeryB.CategoryId
inner join doctor d on SurgeryB.doctorid = d.doctorid
WHERE ISNULL(CounsB.IsCancelled,0)=0
order by p.patientid
;with cte as
(
select Patient, BookingId, BookingDate, CategoryName, Doctor, ROW_NUMBER() over (partition by Patient, GroupId order by BookingDate) row_num
from #temp t
)
select Patient, BookingId, BookingDate, CategoryName, Doctor from cte where row_num = 1
drop table #temp
HTH!

I have an answer to the query you gave as an example, which is described in business rule 1 and to the one in business rule 6.
I made some slight changes to your tables, since I had to re-create them to test the query anyway.
create table patient
(
id int,
name varchar(8)
);
create table doctor
(
id int,
name varchar(8)
);
create table category
(
id int,
name varchar(20),
counseling_or_surgery varchar(10)
);
create table map
(
counseling_category_id int,
surgery_category_id int,
group_id int
);
create table booking
(
id int,
patient_id int,
category_id int,
doctor_id int,
booking_date date,
cancelled bit
);
Here are the inserts to set up the data:
insert into patient (id, name) values (1, 'Alex');
insert into patient (id, name) values (2, 'John');
insert into patient (id, name) values (3, 'Mark');
insert into patient (id, name) values (4, 'Stephen');
insert into doctor (id, name) values (1, 'Doctor 1');
insert into doctor (id, name) values (2, 'Doctor 2');
insert into doctor (id, name) values (3, 'Doctor 3');
insert into category (id, name, counseling_or_surgery) values (1, 'Counseling Dental', 'Counseling');
insert into category (id, name, counseling_or_surgery) values (2, 'Counseling Nose', 'Counseling');
insert into category (id, name, counseling_or_surgery) values (3, 'Dental surgery', 'Surgery');
insert into category (id, name, counseling_or_surgery) values (4, 'Teeth repair', 'Surgery');
insert into category (id, name, counseling_or_surgery) values (5, 'Nose Surgery', 'Surgery');
insert into category (id, name, counseling_or_surgery) values (6, 'Nose adjustment', 'Surgery');
insert into map (counseling_category_id, surgery_category_id, group_id) values (1, 3, 1);
insert into map (counseling_category_id, surgery_category_id, group_id) values (1, 4, 1);
insert into map (counseling_category_id, surgery_category_id, group_id) values (2, 5, 2);
insert into map (counseling_category_id, surgery_category_id, group_id) values (2, 6, 2);
insert into booking (id, patient_id, category_id, doctor_id, booking_date, cancelled) values ( 1, 1, 1, 1, '2017-11-20', 'false');
insert into booking (id, patient_id, category_id, doctor_id, booking_date, cancelled) values ( 2, 1, 3, 3, '2017-11-25', 'false');
insert into booking (id, patient_id, category_id, doctor_id, booking_date, cancelled) values ( 3, 1, 4, 2, '2017-11-25', 'false');
insert into booking (id, patient_id, category_id, doctor_id, booking_date, cancelled) values ( 4, 1, 2, 1, '2017-11-21', 'true');
insert into booking (id, patient_id, category_id, doctor_id, booking_date, cancelled) values ( 5, 1, 2, 2, '2017-11-26', 'false');
insert into booking (id, patient_id, category_id, doctor_id, booking_date, cancelled) values ( 6, 1, 5, 3, '2017-11-28', 'false');
insert into booking (id, patient_id, category_id, doctor_id, booking_date, cancelled) values ( 7, 3, 2, 2, '2017-11-26', 'false');
insert into booking (id, patient_id, category_id, doctor_id, booking_date, cancelled) values ( 8, 3, 6, 2, '2017-11-26', 'true');
insert into booking (id, patient_id, category_id, doctor_id, booking_date, cancelled) values ( 9, 4, 2, 2, '2017-11-29', 'false');
insert into booking (id, patient_id, category_id, doctor_id, booking_date, cancelled) values (10, 4, 5, 1, '2017-11-29', 'false');
Given these tables, here is the query for business rule 1:
select
c.name as patient,
b.id as booking_id,
b.booking_date as first_surgery_date,
d.name as first_surgery,
e.name as doctor
from
(
select
r.patient_id,
min(r.id) as booking_id,
t.group_id
from
booking r
join
category s
on r.category_id = s.id
join
map t
on t.surgery_category_id = r.category_id
where
r.cancelled = 0 and
s.counseling_or_surgery = 'Surgery'
group by
r.patient_id,
t.group_id
) a
join
booking b
on a.booking_id = b.id
join
patient c
on b.patient_id = c.id
join
category d
on b.category_id = d.id
join
doctor e
on b.doctor_id = e.id;
and the results are:
patient booking_id first_surgery_date first_surgery doctor
---------------- ----------- ------------------ ---------------- ----------------
Stephen 10 2017-11-29 Nose Surgery Doctor 1
Alex 2 2017-11-25 Dental surgery Doctor 3
Alex 6 2017-11-28 Nose Surgery Doctor 3
(3 rows affected)
Here's the query for business rule 6. It counts cancellations too; easy to change if that's not what you want.
select
z.name as doctor,
y.total_counseling_bookings,
x.total_surgery_bookings
from
(
select
doctor_id,
count(*) as total_surgery_bookings
from
booking a
join
category b
on a.category_id = b.id
where
b.counseling_or_surgery = 'Surgery'
group by
doctor_id
) x
full outer join
(
select
doctor_id,
count(*) as total_counseling_bookings
from
booking c
join
category d
on c.category_id = d.id
where
d.counseling_or_surgery = 'Counseling'
group by
doctor_id
) y
on x.doctor_id = y.doctor_id
join
doctor z
on x.doctor_id = z.id
and here are the results for this one:
doctor total_counseling_bookings total_surgery_bookings
-------- ------------------------- ----------------------
Doctor 1 2 1
Doctor 2 3 2
Doctor 3 NULL 2
(3 rows affected)

Related

Pivoting a table with SQL

I have a table with position (junior, senior), salary, and an ID. I have done the following to find the highest salary for each position.
SELECT position, MAX(salary) FROM candidates GROUP BY position;
What I am getting:
How I want it:
I want to transpose the outcome so that 'junior' and 'senior' are the columns without using crosstab. I have looked at many pivot examples but they are done on examples much more complex than mine.
I am not proficient in PostgreSQL, but I believe there is a practical workaround solution since this is a simple table:
SELECT
max(case when position = 'senior' then salary else null end) senior,
max(case when position = 'junior' then salary else null end) junior
FROM payments
It worked with this example:
create table payments (id integer, position varchar(100), salary int);
insert into payments (id, position, salary) values (1, 'junior', 1000);
insert into payments (id, position, salary) values (1, 'junior', 2000);
insert into payments (id, position, salary) values (1, 'junior', 5000);
insert into payments (id, position, salary) values (1, 'junior', 3000);
insert into payments (id, position, salary) values (2, 'senior', 3000);
insert into payments (id, position, salary) values (2, 'senior', 8000);
insert into payments (id, position, salary) values (2, 'senior', 9000);
insert into payments (id, position, salary) values (2, 'senior', 7000);
insert into payments (id, position, salary) values (2, 'senior', 4000);
select
max(case when position = 'junior' then salary else 0 end) junior,
max(case when position = 'senior' then salary else 0 end) senior
from payments;
Here is my attempt at teaching myself crosstab:
CREATE EXTENSION IF NOT EXISTS tablefunc;
select Junior
, Senior
from
(
select *
from crosstab
(
'select 1, position, max(salary)
from candidates
group by position
'
, $$VALUES('Junior'), ('Senior')$$
)
as ct(row_number integer, Junior integer, Senior integer) --I don't know your actual data types, so you will need to update this as needed
) q
Edit: Below is no longer relevant as this appears to be PostgreSQL
Based on your description, it sounds like you probably want a pivot like this:
select q.*
from
(
select position
, salary
from candidates
) q
pivot (
max(salary) for position in ([Junior], [Senior])
) p
This example was made in SQL Server since we don't know DBMS.
It depends on which SQL dialect you are running. It also depends on the complexity of your table. In SQL Server, I believe you can use the solutions provided in this question for relatively simple tables: Efficiently convert rows to columns in sql server

sql previous row

I have a SQL question, typical :previous row, next row question BUT:
NOT USING rownum,lead or rankover these functions, only select and join,
Table: Student
Fields: Student_ID, Department, Start_Date
ex:
1,C, 2017-01-1
1,B, 2017-07-1
1,A, 2017-12-1
Expected Output:
Student_ID, Department, Start_Date, End_Date
ex:
1,C, 2017-01-1, 2017-07-01
1,B, 2017-07-1,2017-12-01
1,A, 2017-12-1, ...
End_Date is the start Date of the next record for the student ID
You could try this:
Data
create table student (
Student_ID int,
Department char(1),
Start_Date date
);
insert into student values (1, 'A', '2017-01-01');
insert into student values (1, 'B', '2017-01-01');
insert into student values (1, 'C', '2017-12-31');
SQL Server
select
student_id,
department,
start_date,
(select top 1 start_date
from student
where student_id = s.student_id
) as end_date
from student s
order by student_id, department;
Example: http://rextester.com/HLL58959
PostgreSQL and MySQL and SQLite
select
student_id,
department,
start_date,
(select start_date
from student
where student_id = s.student_id
limit 1) as end_date
from student s
order by student_id, department;
Example: http://rextester.com/XWUAZ90711

SQL: How to get the row when max is on more than 1 column

I am working with Microsoft SQL Server 2008 R2.
I have a table named employee:
create table employee (
employee_id bigint not null primary key,
first_name varchar(50) not null,
middle_name varchar(50) null,
last_name varchar(50) not null
)
I have a table named eligibility. It has a FK to employee table. It has a unique key comprise of 3 columns: employee_id + effective_date + sequence_number.
create table eligibility (
eligibility_id bigint not null primary key,
employee_id bigint not null foreign key references employee (employee_id),
effective_date date not null,
sequence_number int not null,
value varchar(20) not null,
constraint UK_eligibility unique (employee_id, effective_date, sequence_number)
)
I have 1 row in employee table with employee_id = 1001:
insert into employee (employee_id, first_name, middle_name, last_name) values (1001, 'A', 'B', 'C')
I have 4 rows in eligibility table for the same employee_id:
insert into eligibility (eligibility_id, employee_id, effective_date, sequence_number, value) values (1, 1001, '2016-04-13', 1, 'NS')
insert into eligibility (eligibility_id, employee_id, effective_date, sequence_number, value) values (2, 1001, '2016-05-25', 1, 'EX')
insert into eligibility (eligibility_id, employee_id, effective_date, sequence_number, value) values (3, 1001, '2016-05-25', 2, 'VR')
insert into eligibility (eligibility_id, employee_id, effective_date, sequence_number, value) values (4, 1001, '2016-06-05', 1, 'LS')
From the eligibility table, for a given date I want to get the row with the max (effective_date + sequence_number) combination which is less than or equal to that given date.
Examples:
For 2016-04-30 date I would want the row with eligibility_id = 1.
For 2016-05-30 date I would want the row with eligibility_id = 3.
For 2016-06-30 date I would want the row with eligibility_id = 4.
I have wrote the query to get the desired results. This is the query for 2016-05-30 date:
select * from eligibility e
where
e.effective_date = (select max(e1.effective_date)
from eligibility e1
where e1.employee_id = e.employee_id and
e1.effective_date <= '2016-05-30') AND
e.sequence_number = (select max(e2.sequence_number)
from eligibility e2
where e2.employee_id = e.employee_id and
e2.effective_date = e.effective_date)
The query is ok but I want to try write it in some different way to get the same results. What other way you would recommend?
Hmmm, I would use row_number():
select e.*
from (select e.*,
row_number() over (partition by employee_id order by effective_date desc, sequence_number desc
) as seqnum
from eligibility e
) e
where seqnum = 1;
This looks to me like TOP-1 with ties:
SELECT TOP 1 WITH TIES *
FROM eligibility e
WHERE e.effective_date <= '2016-05-30'
ORDER BY e.effective_date DESC, sequence_number DESC

SQL: Employees with more than one department at the same time

I am in need of a query that returns Employee ID, Department, Begin date, End date for the employee who may be registered in more than one department at the same time, meaning:
The Employee1 could be working in
department A: from 01.01.2015 to 31.12.2015 and
department B: from 01.01.2015 to 31.12.2015
(the dates overlap)
OR
department A from 01.01.2015 to 31.12.2015 and
department B from 01.06.2015 to 31.12.2018
(the dates partially overlap)
My tables are something like this
Employee (Employee ID)
Career (Employee ID, Department ID, BeginDate, EndDate)
Department (DepartmentID)
The data that this tables contain are only IDs.
The result should be something like:
EmployeeID Department BeginDate EndDate
1 HR 01.01.2015 31.12.2015
1 ITD 01.01.2015 31.12.2015
2 MR 01.03.2014 31.12.2018
2 HR 01.06.2014 31.12.2016
With the current department column being an ID like (12HDGH4376SHFJ48).
I am not sure how to write this query. I am not sure what to use in the where clause or if it needs sub queries etc. I am a little bit lost with this. Also I work on Oracle. Thank you for any advice.
You can try with something like this:
setup:
create table Employee(EmployeeID number);
create table career(EmployeeID number, DepartmentID number, BeginDate date, EndDate date);
create table department(DepartmentID number, department varchar2(16));
insert into department values (1, 'HR');
insert into department values (2, 'IT');
insert into employee values (20);
insert into employee values (10);
insert into career values (10, 1, to_date('01.01.2015', 'dd.mm.yyyy'), to_date('31.12.2015', 'dd.mm.yyyy'));
insert into career values (10, 2, to_date('01.01.2015', 'dd.mm.yyyy'), to_date('31.12.2015', 'dd.mm.yyyy'));
insert into career values (20, 1, to_date('01.01.2015', 'dd.mm.yyyy'), to_date('31.12.2015', 'dd.mm.yyyy'));
insert into career values (20, 2, to_date('01.06.2015', 'dd.mm.yyyy'), to_date('31.12.2018', 'dd.mm.yyyy'));
query:
select employeeId, departmentId, beginDate, endDate
from (
select employeeId, departmentId, beginDate, endDate,
lag(endDate) over (partition by employeeId order by beginDate) as previousEndDate,
lead(beginDate) over (partition by employeeId order by beginDate) as nextBeginDate
from career
inner join department
using(departmentId)
)
where previousEndDate between beginDate and endDate OR
nextBeginDate between beginDate and endDate
This orders the rows by date and checks, for every row, if it overlaps with the preceeding or the next one, with the same employee
Are you using Oracle, Mysql etc?
Also are you querying 3 tables?
Depending on what you are using you should use a rank to identify the record that is the latest one or the ones that is currently active for the person.
Select
RANK() OVER (partition by Employee ID BY ORDER BY NVL(END_DATE, CURRENT_DATE)DESC) AS RANK.
The rank value will give you the latest record. if you then sub query the whole thing you can then have a where clause like so.
Where a.rank = 1
a being the alias of the sub query.

Counting the instances of customers

Say that I have a table with one column named CustomerId.
The example of the instance of this table is :
CustomerId
14
12
11
204
14
204
I want to write a query that counts the number of occurences of customer IDs.
At the end, I would like to have a result like this :
CustomerId NumberOfOccurences
14 2
12 1
11 1
204 2
14 1
I cannot think of a way to do this.
This is the most basic example of GROUP BY
SELECT CustomerId, count(*) as NumberOfOccurences
FROM tablex GROUP BY CustomerId;
Practice exercise #3 on this page explains how to do this.
CREATE TABLE customers
( customer_id number(10) not null,
customer_name varchar2(50) not null,
city varchar2(50),
CONSTRAINT customers_pk PRIMARY KEY (customer_id)
);
INSERT INTO customers (customer_id, customer_name, city)
VALUES (7001, 'Microsoft', 'New York');
INSERT INTO customers (customer_id, customer_name, city)
VALUES (7002, 'IBM', 'Chicago');
INSERT INTO customers (customer_id, customer_name, city)
VALUES (7003, 'Red Hat', 'Detroit');
INSERT INTO customers (customer_id, customer_name, city)
VALUES (7004, 'Red Hat', 'New York');
INSERT INTO customers (customer_id, customer_name, city)
VALUES (7005, 'Red Hat', 'San Francisco');
INSERT INTO customers (customer_id, customer_name, city)
VALUES (7006, 'NVIDIA', 'New York');
INSERT INTO customers (customer_id, customer_name, city)
VALUES (7007, 'NVIDIA', 'LA');
INSERT INTO customers (customer_id, customer_name, city)
VALUES (7008, 'NVIDIA', 'LA');
Solution:
The following SQL statement would return the number of distinct cities for each customer_name in the customers table:
SELECT customer_name, COUNT(DISTINCT city) as "Distinct Cities"
FROM customers
GROUP BY customer_name;
It would return the following result set:
CUSTOMER_NAME Distinct Cities
IBM 1
Microsoft 1
NVIDIA 2
Red Hat 3