Select users based on their date joined and date left - sql

I have the following table:
Person
UserID Name Date_Joined Date_Left
1 Test 2018-08-10 NULL
2 Test2 2018-07-10 NULL
3 Test3 2018-07-10 2018-12-31
4 Test4 2018-08-10 2018-09-10
I want to check by only their join and/or left date if they are billable(=active) or not.
These are billable users:
User whose start month is at least one month before billdate
with no date left
User whose start month is at least one month before billdate
with date left month equal to the billdate or later than the
billdate
Month of billing = always the previous month.
I use the following query:
DECLARE #billdate AS DATE = '2018-09-01';
SELECT *
FROM person
WHERE CompanyID = 1205 AND (
(
date_joined <= EOMONTH(#billdate, -1)
)
OR
(
date_left > EOMONTH(#billdate, -1) AND
date_left <= EOMONTH(#billdate)
)
)
My problems:
User Test4 is still present in my table if I set the billdate to 2018-11-01.
User Test3 dissapears if i set billdate to 2019-01-01
What is wrong with my query and how can I optimize this?
Sample data:
User list:
1 - Test - 2018-08-10 - NULL
2 - Test2 - 2018-07-10 - NULL
3 - Test3 - 2018-07-10 - 2018-12-31
4 - Test4 - 2018-08-10 - 2018-09-10
For the bill period of the previous month (= 8 / August) = #billdate 2018-09-10, these are the billable users:
Test2
Test3
However, when I change the bill period to 10 / october, these are the billable users:
Test
Test2
Test3

If I understand your logic correctly, a billable user is one whose start month is at least one month before the current month, and whose end month is less than or equal to the current month.
SELECT *,
CASE WHEN #billdate >= DATEADD(d, 1, EOMONTH(date_joined)) AND
(#billdate <= DATEADD(d, 1, EOMONTH(date_left)) OR date_left IS NULL)
THEN 'billable'
ELSE 'not billable' END AS status
FROM person;
This billing logic appears to be consistent with customers getting the first month free, but being billable from the second up to the final month.
Demo

use make flag comparing join month and year with bill month and year and create a flag for each user then use sub-query for filter
select * from t
(
select *,case when (month(date_joined)=month(billdate)
and year(date_joined)=year(billdate)) then 'N' else 'Y' end as flag_billable
from user
) as t where t.flag_billable='Y'
For your query
DECLARE #billdate AS DATE = '2018-09-01';
select * from
(
SELECT * ,case when (month(date_joined)=month(#billdate)
and year(date_joined)=year(#billdate)) then 'N' else 'Y' end as flag_billable
FROM person
) t where t.flag_billable='Y'

Related

SQL derived attribute

I want to make the Room_Status in Table 1 (ROOM) table into a derived attribute base on the check_in and check_out date Table 2 (Booking Records) table, but I don't know if it is possible to determine the room status dynamically based on the check_in/check_out date in table 2 , eg. The room with room_no 103 suppose to be unavailable for day 19/02/2020 to 20/02/2020 because its already booked by someone else , so the room status will be displayed as unavailable or N, after date 20/02/2020 the room will be available again.
Another extra thing is that I want to calculate the days available for each room based on the table 2 check_in and check_out date eg. room 103 will be available for only 1 day if it is booked on 19/02/2020 to 20/02/2020 after one day which is 22/02/2020 the room is booked by another customer, how should I calculate the days available...
Table 1 (ROOM)
ROOM_NO ROOM_STATUS ('Y' represent 'Available' , 'N' represent 'Unavailable')
======= ============
1 Y
2 Y
3 Y
4 Y
5 Y
6 Y
7 Y
8 Y
9 Y
10 Y
more rooms.....
Table 2 (Booking Records)
BOOKING_ID CHECK_IN CHECK_OUT SPECIAL_REQ CANCEL_REASON DATE_BOOK ROOM_NO GUEST
========== ======== ========= =========== ============= ========= ======= =====
1 19/02/2020 20/02/2020 Prepare hot bath tub 17/02/2020 103 980315070652
when check in.
2 20/05/2020 27/05/2020 Prepare scented 10/05/2020 10 C00001549
candle and meal when
check in
3 21/05/2020 23/05/2020 Prepare latest news 10/05/2020 9 C00001894
paper in room
4 20/05/2020 24/05/2020 Prepare hot bath tub 17/05/2020 124 980315070652
when check in.
Not sure I'm following, something like this? Strictness of the inequality (<,<=) will depend on how you want to define available on a given day.
SELECT DISTINCT
Room_No,
CASE WHEN EXISTS
( SELECT 1
FROM Booking_Records BR2
WHERE BR.room_no = BR2.room_no
AND CURRENT_DATE >= BR2.Check_In
AND CURRENT_DATE < BR2.Check_Out
) THEN 'N'
ELSE 'Y'
END AS Currently_Available
FROM Booking_Records BR
This will get the rooms from the booking_records table that are available on 2020-05-22:
SELECT room_no,
CASE COUNT(
CASE
WHEN DATE '2020-05-22' BETWEEN CHECK_IN AND CHECK_OUT
AND cancel_reason IS NULL
THEN 1
END
)
WHEN 0
THEN 'Y'
ELSE 'N'
END AS room_status_20200522
FROM booking_records
GROUP BY room_no
Which, for your sample data:
CREATE TABLE booking_records ( BOOKING_ID, CHECK_IN, CHECK_OUT, CANCEL_REASON, ROOM_NO ) AS
SELECT 1, DATE '2020-02-19', DATE '2020-02-20', CAST( NULL AS VARCHAR2(50) ), 103 FROM DUAL UNION ALL
SELECT 2, DATE '2020-05-20', DATE '2020-05-27', NULL, 10 FROM DUAL UNION ALL
SELECT 3, DATE '2020-05-21', DATE '2020-05-23', NULL, 9 FROM DUAL UNION ALL
SELECT 4, DATE '2020-05-20', DATE '2020-05-24', NULL, 124 FROM DUAL;
Outputs:
ROOM_NO | ROOM_STATUS_20200522
------: | :-------------------
9 | N
103 | Y
10 | N
124 | N
db<>fiddle here
So, You need to fetch room status and number of day availability after room is free.
I am considering that booking_records table can contain more than one reservation for the same room_no.
Following query will give you desired details as of sysdate.
Select r.room_no,
Max(Case when b.bookin_id is not null then'N' else 'Y' end) as booking_status,
Min(bn.check_in - coalesce(b.check_out,sysdate)) as available_days
From rooms r
Left Join booking_records b
On b.room_no = r.room_no
And sysdate between b.check_in and b.check_out
Left join booking_records bn
On bn.room_no = r.room_no
And bn.check_in > sysdate
Group by r.room_no

how to fetch count data of 2 date fields in same month in SQL

I am trying to create a query where I have 3 column.
C_Time: contains task Creation date time
Done_Time: Contains Task completion date time
User ID: Unique id of user
I want to get result where I want to get total count of created tasks in particular month and total number of done task at that same month grouped by user id
Output will be like:
UserID | CreatedCount | DoneCount
------------------------------------------
U12 | 12 | 12
-------------------------------------------
U13 | 7 | 5
here U12 user have created 12 tasks and completed 12 tasks in January 2020 month. But user U13 created 7 tasks in Jan 2020 and done 5 tasks in same month.
You can use apply to unpivot the data and then aggregation:
select t.user_id, sum(is_create), sum(is_complete)
from t cross apply
(values (t.c_time, 1, 0), (t.done_time, 0, 1)
) v(t, is_create, is_complete)
where v.t >= '2020-01-01' and v.t < '2020-02-01'
group by t.user_id;
You can also do this with conditional aggregation:
select user_id,
sum(case when c_time >= '2020-01-01' and c_time < '2020-02-01' then 1 else 0 end),
sum(case when done_time >= '2020-01-01' and done_time < '2020-02-01' then 1 else 0 end)
from t
group by user_id;
This is probably a little faster for your particular example. However, the first version is more generalizable -- for instance, it allows you to summarize easily by both user and month.

SQL query to find out number of days in a week a user visited

I'd like to find out how many days in a week users have visited my site. For example, 1 day in a week, 2 days in a week, every day of the week (7).
I imagine the easiest way of doing this would be to set the date range and find out the number of days within that range (option 1). However, ideally I'd like the code to understand a week so I can run a number of weeks in one query (option 2). I'd like the users to be unique for each number of days (ie those who have visited 2 days have also visited 1 day but would only be counted in the 2 days row)
In my database (using SQLWorkbench64) I have user ids (id) and date (dt)
I'm relatively new to SQL so any help would be very much appreciated!!
Expected results (based on total users = 5540):
Option 1:
Number of Days Users
1 2000
2 1400
3 1000
4 700
5 300
6 100
7 40
Option 2:
Week Commencing Number of Days Users
06/05/2019 1 2000
06/05/2019 2 1400
06/05/2019 3 1000
06/05/2019 4 700
06/05/2019 5 300
06/05/2019 6 100
06/05/2019 7 40
You can find visitor count between a date range with below script. Its also consider if a visitor visits multi days in the given date range, s/he will be counted for the latest date only from the range-
Note: Dates are used as sample in the query.
SELECT date,COUNT(id)
FROM
(
SELECT id,max(date) date
FROM your_table
WHERE date BETWEEN '04/21/2019' AND '04/22/2019'
GROUP BY ID
)A
GROUP BY date
You can find the Monday of the week of a date and then group by that. After you have the week day there is a series of group by. Here is how I did this:
DECLARE #table TABLE
(
id INT,
date DATETIME,
MondayOfWeek DATETIME
)
DECLARE #info TABLE
(
CommencingWeek DATETIME,
NumberOfDays INT,
Users INT
)
INSERT INTO #table (id,date) VALUES
(1,'04/15/2019'), (2,'07/21/2018'), (3,'04/16/2019'), (4,'04/16/2018'), (1,'04/16/2019'), (2,'04/17/2019')
UPDATE #table
SET MondayOfWeek = CONVERT(varchar(50), (DATEADD(dd, ##DATEFIRST - DATEPART(dw, date) - 6, date)), 101)
INSERT INTO #info (CommencingWeek,NumberOfDays)
SELECT MondayOfWeek, NumberDaysInWeek FROM
(
SELECT id,MondayOfWeek,COUNT(*) AS NumberDaysInWeek FROM #table
GROUP BY id,MondayOfWeek
) T1
SELECT CommencingWeek,NumberOfDays,COUNT(*) AS Users FROM #info
GROUP BY CommencingWeek,NumberOfDays
ORDER BY CommencingWeek DESC
Here is the output from my query:
CommencingWeek NumberOfDays Users
2019-04-14 00:00:00.000 1 2
2019-04-14 00:00:00.000 2 1
2018-07-15 00:00:00.000 1 1
2018-04-15 00:00:00.000 1 1

SQL Query to return Month days as Columns and Total Hours as Row

i am working on an Attendance report were I need to create a SQL Query from one table to return the attendance of employees net hours worked over the month.
Day of the month should be as a column and in the rows should be the total Hours of employee.
The Table is having 6 Columns ( Employee Name, Dept , Position, Time In , Time Out and Total Hours
Picture for Selecting * From the Attendance Table
i want to return the values as the following:
EmployeeName | 1st | 2nd | 3rd | 4th | ...... |30 June
Emp 1 | 10:30 | | 10:40 | | 10:10 | | 10:21 |
The Days column should be returned in a parameter so i can add it to crystal report.
Table Structure
if you can advise please.
Thanks in advance
You can use CASE statment like this:
SELECT EmployeeName,
(CASE WHEN EXTRACT(YEAR FROM DATE) = 2017 AND EXTRACT(MONTH FROM DATE) = 6 AND EXTRACT(DAY FROM DATE) = 1 then totalHours ELSE NULL END) AS "01/06",
(CASE WHEN EXTRACT(YEAR FROM DATE) = 2017 AND EXTRACT(MONTH FROM DATE) = 6 AND EXTRACT(DAY FROM DATE) = 2 then totalHours ELSE NULL END) AS "02/06",
.
.
.
(CASE WHEN EXTRACT(YEAR FROM DATE) = 2017 AND EXTRACT(MONTH FROM DATE) = 6 AND EXTRACT(DAY FROM DATE) = 30 then totalHours ELSE NULL END) AS "30/06"
FROM Attendance
So, for each day a new column will be created.
I used something like this
CREATE TABLE `AxsLog` (
`id` integer NOT NULL UNIQUE,
`Logon` text NOT NULL DEFAULT current_timestamp,
`Logoff` text NOT NULL DEFAULT current_timestamp,
`Duration` text NOT NULL DEFAULT 0,
`SysDat` text NOT NULL DEFAULT current_timestamp,
PRIMARY KEY(`id`) );
You can easily add an FK column for each row in your user table.
Keep the logon id for each entry, then update that line on logoff
UPDATE AxsLog
Set Duration= (SELECT sum( strftime('%s', logoff) - strftime('%s', logon) )
/60 FROM AxsLog WHERE id= 1 )
WHERE id= 1 ;
To build a report, use something like this. This query only gives a total per month.
select total(Duration)
FROM AxsLog where substr(sysdat,6,2) = 'month'
your requirement can be fulfill by using crosstab report or if u want to achieve in sql then use pivot

how to count days between two dates with where conditions

i have table and it has following data:
USERID NAME DATEFROM DATETO
1 xxx 2014-05-10 2014-05-15
1 xxx 2014-05-20 2014-05-25
4 yyy 2014-04-20 2014-04-21
now i have sql query like :
select * from leave where datefrom>='2014-05-01' and dateto<='2014-05-31'
so now i want output :
userid name total_leave_days
1 xxx 12
4 yyy 2
(2014-05-10 - 2014-05-15 )=6 days
(2014-05-20 - 2014-05-25 )=6 days
total = 12 days for useid 1
(2014-04-20 - 2014-04-21)= 2 days for userid 4
how can i calculate this total days .?
Please try:
select
USERID,
NAME,
SUM(DATEDIFF(day, DATEFROM, DATETO)+1) total_leave_days
From leave
group by USERID, NAME
SQL Fiddle Demo
It's important to note that you need "+1" to emulate the expected calculations because there is an inherent assumption of ""start of day" for the Start date and "end of day" for end date - but dbms's don't think that way. a date is always stored as "start of day".
select
USERID
, name
, sum( datediff(day,DATEFROM,DATETO) + 1 ) as leave_days
from leavetable
group by
USERID
, name
produces this:
| USERID | NAME | LEAVE_DAYS |
|--------|------|------------|
| 1 | xxx | 12 |
| 4 | yyy | 2 |
see: http://sqlfiddle.com/#!3/ebe5d/1
You can use DateDiff.
SELECT UserID, Name, SUM(DATEDIFF(DAY, DateFrom, DateTo) + 1) AS total_leave_days
FROM leave
WHERE datefrom >= '2014-05-01' AND dateto <= '2014-05-31'
GROUP BY UserID, Name
The + 1 ,of course, is because DATEDIFF will return the exclusive count, where it sounds like you want the inclusive number of days.
Try this:
select userid, name, sum (1 + datediff(day,datefrom,dateto)) as total_leave_days
from leaves
where datefrom>='2014-05-01' and dateto<='2014-05-31'
group by userid, name
This will sum the total leaves per userid. Note that datediff will give you 5 days difference for the range 2014-05-10 to 2014-05-15, so we need to add 1 to the result to get 6 days i.e. range inclusive of both ends.
Demo