Compare start and end dates across multiple rows - sql

I have a table of subscriptions for contacts. A contact can have multiple subscriptions:
CREATE TABLE contact (
id INTEGER NOT NULL,
name TEXT,
PRIMARY KEY (id)
);
CREATE TABLE subscription (
id INTEGER NOT NULL,
contact_id INTEGER NOT NULL REFERENCES contact(id),
start_date DATE,
end_date DATE,
PRIMARY KEY (id)
);
I need to get all subscriptions for a given by contact that do not have a subscription
that starts on the same date as the end date of the another subscription for the same
contact.
So for the given data:
INSERT INTO contact (id, name) VALUES
(1, 'John'),
(2, 'Frank');
INSERT INTO subscription (id, contact_id, start_date, end_date) VALUES
(1, 1, '2012-01-01', '2013-01-01'),
(2, 1, '2013-01-01', '2014-01-01'),
(3, 2, '2012-01-01', '2012-09-01'),
(4, 2, '2013-01-01', '2014-01-01');
I want to get subscriptions with ids of 2, 3, 4 but not 1, because the contact 'John'
has a subscription with a start_date on the same day (2013-01-01) as the end_date
for subscription with id of 1.
What is the best way to achieve this?

SQL Fiddle
select *
from subscription s0
where not exists (
select 1
from subscription s1
where
s0.contact_id = s1.contact_id
and s1.start_date = s0.end_date
)
order by contact_id, id

Related

How to list total number of scholarships per department in SQL

I have 2 tables that look like this where I want to query how many scholarships (from Tuition table) each department (from Student table) has distributed:
I am thinking a join is necessary but am not sure how to do so.
Create tables
create table students (
sid int auto_increment primary key,
name varchar(100),
email varchar(100),
department varchar(100)
);
create table tutions (
id int auto_increment primary key,
sid int,
cost int,
scholarships int,
duedate timestamp default current_timestamp
);
Sample data
insert into students (name, email, department)
values
('John Doe', 'john#abc.xyz', 'B'),
('Jane Doe', 'jane#abc.xyz', 'A'),
('Jack Doe', 'jack#abc.xyz', 'C'),
('Jill Doe', 'jill#abc.xyz', 'B');
insert into tutions (sid, cost, scholarships)
values
(1, 1000, 2),
(2, 1000, 1),
(3, 1000, 7),
(4, 1000, 2);
Query (department-wise total scholarships)
SELECT department, sum(scholarships) as scholarships
FROM students s
JOIN tutions t ON s.sid = t.sid
GROUP BY department
Output
Running SQL Fiddle
Not sure It's something you want? And not sure scholarships is a number or name of scholarship? So I doubt it's a name as varchar string type.
### dummy record
CREATE TABLE students (
psu_id INTEGER PRIMARY KEY,
firstname VARCHAR NOT NULL,
lastname VARCHAR NOT NULL,
email VARCHAR NOT NULL,
department VARCHAR NOT NULL
);
CREATE TABLE tuition (
tuition_id INTEGER PRIMARY KEY,
student_id INTEGER NOT NULL,
semeter_cost INTEGER NOT NULL,
scholarships VARCHAR NOT NULL,
due_date DATE NOT NULL
);
INSERT INTO students VALUES (1, 'John', 'Hello', 'Jonh#email.com', 'Engineering');
INSERT INTO students VALUES (2, 'Bella', 'Fuzz', 'Bella#email.com', 'Computer');
INSERT INTO students VALUES (3, 'Sunny', 'World', 'Sunny#email.com', 'Science');
INSERT INTO tuition VALUES (1, 1, 4000, 'first_class_en', '2022-05-09' );
INSERT INTO tuition VALUES (2, 2, 3000, 'nobel', '2022-05-09' );
INSERT INTO tuition VALUES (3, 3, 5000, 'hackathon', '2022-05-09' );
INSERT INTO tuition VALUES (4, 1, 4500, 'second_class_en', '2022-05-09' );
-----------------
### query
SELECT s.department, count(t.scholarships)
FROM students s
JOIN tuition t
ON s.psu_id = t.student_id
GROUP BY s.department
### output
department, total_scholarships
Computer|1
Engineering|2
Science|1

SQLite calculating difference of date got incorrect result

I am having trouble calculating date difference in SQLite.
I've set the value type to timestamp when setting up the tables, but the calculation for date seems only apply to the first number of my date entry.
I've try to use to_date('01/01/2020', 'mm/dd/yyyy') but then it return error saying not support to_date. My code is below, any suggestion would be much appreciated.
CREATE TABLE customer_join
(
id INT,
country_code VARCHAR(10),
country_descrip VARCHAR(255),
register_date TIMESTAMP,
customer_id INT,
PRIMARY KEY (id),
FOREIGN KEY (customer_id) REFERENCES customer(id)
);
CREATE TABLE customer_order
(
id INT,
item_name VARCHAR(25),
item_description VARCHAR(255),
number FLOAT(24),
order_date TIMESTAMP,
customer_id INT,
PRIMARY KEY (id),
FOREIGN KEY (customer_id) REFERENCES patient(id)
);
INSERT INTO customer_join
Values (1, 1, 'none', '1/22/2017', 100),
(2, 1, 'none', '1/23/2017', 101),
(3, 1, 'none', '1/24/2017', 102),
(4, 1, 'none', '1/25/2017', 103),
(5, 1, 'none', '1/26/2017', 104),
(6, 2, 'none', '1/27/2017', 101),
(7, 2, 'none', '1/28/2017', 106),
(8, 1, 'none', '1/29/2017', 107);
INSERT INTO customer_order
Values (1, 'A', 'none', 1, '2/23/2020', 101),
(2, 'B', 'none', 1, '3/11/2027', 100),
(3, 'B, C, D', 'none', 1, '4/10/2023', 100),
(4, 'B, C, E', 'none', 1, '4/11/2020', 100),
(5, 'R', 'none',1, '4/12/2099', 102);
SELECT (order_date - register_date) TIME_TO_ORDER
FROM customer_join cj
INNER JOIN
(SELECT customer_id , MIN(order_date) order_date
FROM customer_order
GROUP BY customer_id) co
ON cj.customer_id = co.customer_id;
The code gives me the result:
TIME_TO_ORDER
2
1
3
1
Which is not I wanted. I was trying to figure out how long does it take for customers to place their first order. Any suggestions?
First, you must change the format of the dates in both tables to YYYY-MM-DD, which is the only valid text date format for SQLite.
Then use the function julianday() to get the difference in days between the dates:
SELECT cj.customer_id,
julianday(co.order_date) - julianday(cj.register_date) TIME_TO_ORDER
FROM customer_join cj
INNER JOIN (
SELECT customer_id , MIN(order_date) order_date
FROM customer_order
GROUP BY customer_id
) co ON cj.customer_id = co.customer_id;
See the demo.
Results:
customer_id | TIME_TO_ORDER
----------: | ------------:
100 | 1175
101 | 1126
102 | 30028

Update table data, fetched from another table

I have a table which is storing the attendance information on an employee and another table that's storing the information about the shift of the employee which is basically a duty roster.
Here is the structure to attendance table
CREATE TABLE Attendance
(
ID INT,
EmpCode INT,
ShiftCode INT,
CheckIn DATETIME,
CheckOut DATETIME
)
INSERT INTO Attendance VALUES (1, 1, 1, '2019-09-01 09:16:23', NULL)
INSERT INTO Attendance VALUES (2, 1, 1, NULL, '2019-09-01 18:01:56')
INSERT INTO Attendance VALUES (3, 1, 2, '2019-09-02 09:00:00', NULL)
INSERT INTO Attendance VALUES (4, 1, 2, NULL, '2019-09-02 18:48:21')
INSERT INTO Attendance VALUES (5, 1, 1, '2019-09-13 09:27:00', NULL)
INSERT INTO Attendance VALUES (6, 1, 1, NULL, '2019-09-13 18:45:00')
INSERT INTO Attendance VALUES (7, 2, 2, '2019-09-01 21:19:17', NULL)
INSERT INTO Attendance VALUES (8, 2, 2, NULL, '2019-09-01 23:30:56')
INSERT INTO Attendance VALUES (9, 2, 2, '2019-09-05 09:23:00', NULL)
INSERT INTO Attendance VALUES (10, 2, 2, NULL, '2019-09-05 17:19:00')
Here is the structure and sample data for Duty roster.
CREATE TABLE Shifts
(
ID INT PRIMARY KEY,
EmpCode INT,
ShiftCode INT,
StartDate DATETIME,
EndDate DATETIME
)
INSERT INTO Shifts VALUES (1, 1, 24, '2019-09-01 00:00:00', '2019-09-05 00:00:00');
INSERT INTO Shifts VALUES (2, 2, 25, '2019-09-01 00:00:00', '2019-09-05 00:00:00');
The idea is to update the ShiftCode in Attendance table wrt to the shifts stored in the duty roster. So if the attendance for employee 1 is between '2019-09-01' and '2019-09-05' then the shift code for this employee should be updated to 24 and same for other employee. If the duty roster does not exist for the dates present in attendance table it should not update it and let it the way it is.
I need an update query.
Something like this:
SELECT *
FROM Attendance A
INNER JOIN Shifts S
ON A.EmpCode = S.[EmpCode]
AND
(
A.CheckIn BETWEEN S.[StartDate] AND S.[EndDate]
OR
A.CheckOut BETWEEN S.[StartDate] AND S.[EndDate]
)
and with update:
UPDATE Attendance
SET ShiftCode = S.[ShiftCode]
FROM Attendance A
INNER JOIN Shifts S
ON A.EmpCode = S.[EmpCode]
AND
(
A.CheckIn BETWEEN S.[StartDate] AND S.[EndDate]
OR
A.CheckOut BETWEEN S.[StartDate] AND S.[EndDate]
);
I have tried this one and it works too:
UPDATE Attendance
SET ShiftCode = ISNULL((SELECT ShiftCode FROM Shifts Roster
WHERE CAST(COALESCE(CheckIn, CheckOut) AS DATE) BETWEEN StartDate AND EndDate AND EmpCode = Attendance.EmpCode),
(SELECT ShiftCode FROM EmployeeInfo WHERE EmployeeInfo.ID = Attendance.EmpCode))
Try this. It will helpful
UPDATE Attendance SET ShiftCode=c.ShiftsShiftCode
FROM Attendance a
JOIN
(
SELECT a.EmpCode, a.ShiftCode, CheckIn, CheckOut, b.ShiftCode AS ShiftsShiftCode FROM Attendance a
JOIN Shifts b ON a.EmpCode=b.EmpCode
AND (a.CheckIn BETWEEN StartDate AND EndDate OR a.CheckOut BETWEEN StartDate AND EndDate)
)c
ON a.EmpCode = c.EmpCode
AND (a.checkin=c.checkin OR a.CheckOut=c.CheckOut)

Get most recent row inserted with the least specificity

I'll first explain the data model then the desired results and what I have tried.
I have vehicles and sales tables:
CREATE TABLE VEHICLE
(
ID INT IDENTITY(1,1) NOT NULL PRIMARY KEY,
BRAND INT NOT NULL,
MODEL VARCHAR(255),
VERSION VARCHAR(255),
UNIQUE(BRAND, MODEL, VERSION),
FOREIGN KEY(BRAND) REFERENCES BRAND(ID)
)
CREATE TABLE SALES
(
ID INT IDENTITY(1,1) NOT NULL PRIMARY KEY,
VEHICLE_ID INT NOT NULL,
DATE DATE NOT NULL,
SALE INT NOT NULL,
CREATED_DATE DATETIME NOT NULL DEFAULT GETDATE(),
FOREIGN KEY (VEHICLE_ID) REFERENCES VEHICLE(ID)
)
This way I can insert several entries for the same vehicle for the same date (when I want to update, I insert a new row)
INSERT INTO SALES (VEHICLE_ID, DATE, SALE, USER_ID)
VALUES (1, '2018-01-01', 2, 3) -- then later i update by inserting a new row
(1, '2018-01-01', 4, 3)
I want to retrieve the last sale inserted for a specific date range (using the DATE), then filter for a specific BRAND, or model or version.
I got it working by doing this
SELECT
S.DATE AS date, SUM(S.SALE_PROJECTION) AS saleProjection
FROM
SALE_PROJECTION S,
(SELECT MAX(ID) AS id
FROM SALE_PROJECTION
WHERE DATE >= CAST(#dateStart AS DATE)
AND DATE <= CAST(#dateEnd AS DATE)
GROUP BY DATE, VEHICLE_ID) S_M,
VEHICLE V
WHERE
1 = 1
AND S.ID = S_M.ID
AND S.VEHICLE_ID = V.ID
AND V.BRAND = 1
AND V.MODEL = 'A6'
AND V.VERSION = '1.0'
GROUP BY S.DATE
ORDER BY DATE
The problem is i want to get the sales for the brand 1 that has the least specificity, meaning:
If i have 3 vehicles:
(1, 'A3', '1.0'),
(1, 'A3', '2.0'),
(1, 'A3', null),
(1, null, null);
if i insert a sale (1, 2018-01-01, 2, 3)
if i insert a sale (2, 2018-01-01, 3, 3) -- the sum for 2018-01-01 would be 5
but then insert a sale for (2, 2018-01-01, 3, 3) -- the sum for 2018-01-01 has to be 3, because it's the last inserted with the least specifity
But the oposite must be true as well
if i insert a sale (3, 2018-01-01, 4, 3)
then insert a sale for (1, 2018-01-01, 1, 3)
then insert a sale for (2, 2018-01-01, 1, 3)
the sum for 2018-01-01 has to be 2, because it's the last inserted
The most general combination of Brand, Model, Version has to "hide" the most specific.
Do i need to change my data model? or this is possible?
I can give more examples if needed.
Thanks in advance

How to retrieve WTD,YTD,MTD users from a user traffic table in the same query?

In a user traffic table as below, I would like to compute the week to date (WTD), month to date ( MTD ), year to date ( YTD ) user and returned user counts.
Test data :
create table user_traffic (session_id number(6), session_day date,
user_id number(6), product_id number(6));
insert into user_traffic values ( 1, date '2016-09-07', 101, 1);
insert into user_traffic values ( 2, date '2016-09-07', 101, 4);
insert into user_traffic values ( 3, date '2016-09-07', 102, 1);
insert into user_traffic values ( 4, date '2016-09-08', 101, 2);
insert into user_traffic values ( 5, date '2016-09-08', 101, 4);
insert into user_traffic values ( 6, date '2016-09-09', 102, 1);
insert into user_traffic values ( 7, date '2016-09-10', 102, 1);
insert into user_traffic values ( 8, date '2016-09-10', 103, 3);
insert into user_traffic values ( 9, date '2016-09-25', 104, 3);
insert into user_traffic values ( 10, date '2016-10-01', 103, 1);
insert into user_traffic values ( 11, date '2016-10-02', 104, 3);
Expected Output :-
Week_Start_Day, WTD_new_cnt, WTD_returned_cnt
Month_Start_Day, MTD_new_cnt, MTD_returned_cnt
Year_Start_Day, YTD_new_cnt, YTD_returned_cnt
Comments :-
For eg: In the above user traffic table userid=104 visited on Oct 02nd and the WTD,MTD,YTD new/returned counts would be as below.
WTD,new,return
2016-09-26(Mon)(Week start day ), 1,0 ( For userid = 104 )
MTD,new,return
2016-09,1,1
2016-10,0,1
YTD,new,return
2016,0,1
What I have tried?
select session_day,
COUNT( distinct user_id ) AS user_cnt,
count(distinct user_id) - lag(count(distinct user_id))
over (order by session_day) gain,
count(newu) AS newu, count(returnu) AS returnu
from
(
select session_id,
session_day,
user_id,
CASE WHEN
count(*) over ( partition by user_id ORDER BY
session_day,session_id ROWS
BETWEEN UNBOUNDED PRECEDING AND
CURRENT ROW
)
= 1
THEN 1
END
AS newu,
CASE WHEN
lag( session_day,1 ) over ( partition by user_id ORDER
BY session_day,session_id
)
<>
lag( session_day,1 ) over ( order by
session_day,session_id
)
THEN 1
END AS returnu
from user_traffic u
)
group by session_day
order by session_day;
I have built this sql in computing the new/returned users from the user traffic table at sessionday level.