How do I phrase this condition in T-SQL syntax? - sql

I have a table in SQL Server 2012 with employees data. An extract is shown below:
empID DateOfEntry DateLeft
102 2015-05-21 2016-04-20
104 2015-05-14 2015-12-28
...
I need to extract all employees who were present during the period 2016-07-01 and 2017-06-30.
To do this, I need to add a WHERE filter on the DateLeft column in my query but I am a bit confused as to how to phrase this logic.

I think this is what you are after:
WHERE DateOfEntry <= '2017-06-30'
and DateLeft >= '2016-07-01'
You can think of your question as "people who started before (or on) your latest date and left after (or on) your Earliest date".

"all employees who were present during the period 2016-07-01 and 2017-06-30"
Your phrasing implies several scenarios so I will address all
If you want employees who were present in these two dates specifically, use IN operator in your where filter:
SELECT *
FROM <Table>
WHERE DateLeft in ('2016-07-01','2017-06-30');
If you want employees who were present through these range of two dates you can use
'between' syntax:
SELECT *
FROM <Table>
WHERE DateLeft between '2017-06-30' and '2016-07-01';
Note that when using 'between' begin and end values are included, so assuming you want to exclude the last date in range for example you will need to use greater then or equal / lower then operators
SELECT *
FROM <Table>
WHERE DateLeft >= '2017-06-30' and DateLeft < '2016-07-01';

I think you need something like this.
DECLARE #Employee TABLE(empID INT,DateOfEntry DATE,DateLeft DATE)
INSERT #Employee
SELECT 101,'2015-05-21','2016-08-20' UNION ALL -- O
SELECT 102,'2015-05-21','2016-04-20' UNION ALL -- X
SELECT 104,'2015-05-14','2015-12-28' UNION ALL -- X
SELECT 105,'2015-05-14','2018-12-28' -- O
DECLARE #StartDate DATE = '2016-07-01'
DECLARE #EndDate DATE = '2017-06-30'
SELECT * FROM #Employee
WHERE
(DATEDIFF(DAY,DateLeft,#StartDate) <=0 AND DATEDIFF(DAY,DateLeft,#EndDate) >= 0)
OR
(DATEDIFF(DAY,DateLeft,#StartDate) <=0 AND DATEDIFF(DAY,DateLeft,#EndDate) <= 0)

Try this:
SELECT *
FROM [mytable]
WHERE [DateOfEntry] >= '2016-07-01'
AND [DateLeft] <= '2017-06-30';
Also, when working with dates and ranges, do not use BETWEEN even some people are going to show you the syntax.
To be precise, you should avoid passing dates as string in this format, too:
YYYY-MM-DD
As it can break under certain scenarios — such as when the user's language settings are set to French:
SET LANGUAGE FRENCH;
GO
SELECT CONVERT(DATETIME, '2009-10-13');
You can use YYYYMMDD instead.
Aaron Bertrand has nice article about dates and dates ranges, part of his series - Bad habits to kick - you can find more details and examples there, if you want :-)

Related

Selecting 2 date sets from the same dataset?

Say I have a query that selects all the sales from the past 90 days. I want to be able to isolate certain rows on a case/when basis, and can't quite figure out how to do this. The case statement is depending on dates, so: If the date falls between 3/1 and 5/31, then I want to select the sales from any month ends (3/31, 4/30, 5/31 and TODAY) otherwise, if the date is not between 3/1 and 5/31, then I just want to select the past 3 month-ends.
What I tried so far is inserting a Case/When statement in the WHERE clause, but that doesn't seem kosher. Is there another way to go about this?
For reference, the #monthends table contains the following single column:
monthends
2019-03-31
2019-02-28
2019-01-31
and the #insideRule table contains similarly:
insiderRule
2019-03-31
2019-04-22
The query:
SELECT *
FROM mytable
WHERE asofdate IN
CASE WHEN asofdate BETWEEN '3-1-2019' AND '5-31-2019' THEN
(SELECT * FROM #insideRule)
ELSE
(SELECT * FROM #monthends)
END
When I execute the above, I get syntax errors around "IN"
You want exists not case expression :
IF EXISTS (SELECT 1 FROM mytable WHERE aasofdate BETWEEN '2019-03-01' AND '2019-05-31')
SELECT *
FROM #insideRule
ELSE
SELECT *
FROM #monthends
I am thinking you want something like this:
SELECT ir.*
FROM #insideRule ir
WHERE getdate() >= '2019-03-01' AND
getdate() < '2019-06-01'
UNION ALL
SELECT me.*
FROM #monthends me
WHERE getdate() < '2019-03-01' OR
getdate() >= '2019-06-01';
This assumes that the two tables have the same columns in the same order with compatible types.

How to calculate the total numbers of employees available as at a specific day given their date of entry?

I am using SQL Server 2012 and I have a table in my database called Employee.
Assuming it has only 3 columns, namely EmpID, Dept, DateOfEntry, Date Left, what would be the SQL query syntax that will give the count of employees as at, say, 2017-05-31
Each row in the table represents a single employee.
Here is how the Employee Table stands:
EmpId Dept DateOfEntry DateLeft
100 F&B 2015-06-05 2016-01-02
125 Kitchen 2016-02-12 2016-03-10
151 Finance 2018-05-03 NULL
...
UPDATE: Apologies for missing the DateLeft column in the above table. I have updated the question accordingly.
It seems that the OP isn't going to post their attempt.
This is a very simple query, and with a bit of a Google/Bing/Yahoo you probably would have found the syntax on how to use a WHERE clause with Greater Than (>) and Less Than (<) expressions. Anyway, the answer you want is:
DECLARE #Date date;
SET #Date = '20170531';
SELECT COUNT(*) AS Employees
FROM YourTable
WHERE DateOfEntry <= #Date
AND (DateLeft > #Date --I assume that they are no longer emplyed on the day they leave, other use >=
OR DateLeft IS NULL);
select count(*),dept from employee where '31-may-2017'>DateOfEntry
and '31_-may-2017'<DateOfLeft group by dept;
If you want at in single date than try this:
select count(EmpID) from employee
where DateOfEntry>='31-may-2017' and (dateleft is null or dateleft< getdate())
You would count the employees who are at work as of that date, right? Meaning they should have entered on or BEFORE that day:
select count(*)
from employees
where DateOfEntry <= '20170531' and
(dateLeft is null OR dateLeft >= '20170531');
Note: If you wouldn't accept the person as at work on her\his dateLeft then change >= to >.

Table not modified since date

I have a table with following fields.
Part ID, Quantity, Last modified date
I am using the following simple query to retrieve data, how can I add more criteria to show that quantity in hand 0 and part did not modified since 01/01/2015 till now.
SELECT *
FROM table001
where quantity_on_hand=0
Just add another filter in your WHERE clause like below:
SELECT *
FROM table001
WHERE quantity_on_hand=0
AND ModificationDate <= '20150101' -- Date format = 'YYYYMMDD'
I think this will do what you're looking for
SELECT * FROM table001 WHERE quantity_on_hand = 0 AND last_modified_date <= '2015-01-01'
If you are looking to combine multiple conditions you could use AND,OR operators after WHERE clause.
Here I think you might be looking for:
SELECT
*
FROM
table001
WHERE
qunatity_on_hand=0 AND last_modified_date <= '2015-01-01';

SQL Server Between Start and End dates

I'm creating an internal holiday booking system and I need to put business logic rules into place but I need to do a check on how many people are booked off on the dates between the Start and End date because for example 2 apprentices may only be booked off on 1 day but I have no way off grabbing the dates between.
Any help would be appreciated
Below is the job role table
You haven't posted the RDBMS, or the names of the tables, or what exactly the Job Role table is supposed to be doing ... but I'll take a shot at this anyway. I'm using a recursive CTE to generate a list of dates, but it would be far better for you to use a Date table, and I don't even know whether your RDBMS will support this. I've also posted the syntax for a table variable below that populates data mimicking your sample.
The final output, naturally, will need to be customized to do whatever you need to do. This does show you, however, a list of every date when more than one employee is on vacation. Add extra conditions to the second JOIN or to the WHERE clause to filter on other things (like JobRole) if necessary.
-- Code originally from http://smehrozalam.wordpress.com/2009/06/09/t-sql-using-common-table-expressions-cte-to-generate-sequences/
-- Define start and end limits
DECLARE #todate DATETIME, #fromdate DATETIME
SELECT #fromdate='2014-01-01', #todate=GETDATE()-1
DECLARE #TimeOff TABLE (StartDate DATETIME, EndDate DATETIME, EmployeeID INT)
INSERT INTO #TimeOff (StartDate, EndDate, EmployeeID)
SELECT '1/1/2014', '1/7/2014', 7 UNION
SELECT '2/1/2014', '2/7/2014', 7 UNION
SELECT '3/3/2014', '3/9/2014', 7 UNION
SELECT '2/5/2014', '2/6/2014', 8
;WITH DateSequence( Date ) AS -- this will list all dates. Use this if you don't have a date table
(
SELECT #fromdate as Date
UNION ALL
SELECT DATEADD(DAY, 1, Date)
FROM DateSequence
WHERE Date < #todate
)
--select result
SELECT DateSequence.Date, TimeOffA.StartDate, TimeOffB.EndDate, TimeOffA.EmployeeID
FROM
DateSequence -- a full list of all possible dates
INNER JOIN
#TimeOff TimeOffA ON -- all dates when an employee is on vacation -- replace this with your actual table's name
DateSequence.Date BETWEEN TimeOffA.StartDate AND TimeOffA.EndDate
INNER JOIN
#TimeOff TimeOffB ON -- all dates when an employee who is NOT employee A is on vacation -- replace this with your actual table's name
DateSequence.Date BETWEEN TimeOffB.StartDate AND TimeOffB.EndDate AND
TimeOffA.EmployeeID <> TimeOffB.EmployeeID
option (MaxRecursion 2000)

SQL for counting events by date

I feel like I've seen this question asked before, but neither the SO search nor google is helping me... maybe I just don't know how to phrase the question. I need to count the number of events (in this case, logins) per day over a given time span so that I can make a graph of website usage. The query I have so far is this:
select
count(userid) as numlogins,
count(distinct userid) as numusers,
convert(varchar, entryts, 101) as date
from
usagelog
group by
convert(varchar, entryts, 101)
This does most of what I need (I get a row per date as the output containing the total number of logins and the number of unique users on that date). The problem is that if no one logs in on a given date, there will not be a row in the dataset for that date. I want it to add in rows indicating zero logins for those dates. There are two approaches I can think of for solving this, and neither strikes me as very elegant.
Add a column to the result set that lists the number of days between the start of the period and the date of the current row. When I'm building my chart output, I'll keep track of this value and if the next row is not equal to the current row plus one, insert zeros into the chart for each of the missing days.
Create a "date" table that has all the dates in the period of interest and outer join against it. Sadly, the system I'm working on already has a table for this purpose that contains a row for every date far into the future... I don't like that, and I'd prefer to avoid using it, especially since that table is intended for another module of the system and would thus introduce a dependency on what I'm developing currently.
Any better solutions or hints at better search terms for google? Thanks.
Frankly, I'd do this programmatically when building the final output. You're essentially trying to read something from the database which is not there (data for days that have no data). SQL isn't really meant for that sort of thing.
If you really want to do that, though, a "date" table seems your best option. To make it a bit nicer, you could generate it on the fly, using i.e. your DB's date functions and a derived table.
I had to do exactly the same thing recently. This is how I did it in T-SQL (
YMMV on speed, but I've found it performant enough over a coupla million rows of event data):
DECLARE #DaysTable TABLE ( [Year] INT, [Day] INT )
DECLARE #StartDate DATETIME
SET #StartDate = whatever
WHILE (#StartDate <= GETDATE())
BEGIN
INSERT INTO #DaysTable ( [Year], [Day] )
SELECT DATEPART(YEAR, #StartDate), DATEPART(DAYOFYEAR, #StartDate)
SELECT #StartDate = DATEADD(DAY, 1, #StartDate)
END
-- This gives me a table of all days since whenever
-- you could select #StartDate as the minimum date of your usage log)
SELECT days.Year, days.Day, events.NumEvents
FROM #DaysTable AS days
LEFT JOIN (
SELECT
COUNT(*) AS NumEvents
DATEPART(YEAR, LogDate) AS [Year],
DATEPART(DAYOFYEAR, LogDate) AS [Day]
FROM LogData
GROUP BY
DATEPART(YEAR, LogDate),
DATEPART(DAYOFYEAR, LogDate)
) AS events ON days.Year = events.Year AND days.Day = events.Day
Create a memory table (a table variable) where you insert your date ranges, then outer join the logins table against it. Group by your start date, then you can perform your aggregations and calculations.
The strategy I normally use is to UNION with the opposite of the query, generally a query that retrieves data for rows that don't exist.
If I wanted to get the average mark for a course, but some courses weren't taken by any students, I'd need to UNION with those not taken by anyone to display a row for every class:
SELECT AVG(mark), course FROM `marks`
UNION
SELECT NULL, course FROM courses WHERE course NOT IN
(SELECT course FROM marks)
Your query will be more complex but the same principle should apply. You may indeed need a table of dates for your second query
Option 1
You can create a temp table and insert dates with the range and do a left outer join with the usagelog
Option 2
You can programmetically insert the missing dates while evaluating the result set to produce the final output
WITH q(n) AS
(
SELECT 0
UNION ALL
SELECT n + 1
FROM q
WHERE n < 99
),
qq(n) AS
(
SELECT 0
UNION ALL
SELECT n + 1
FROM q
WHERE n < 99
),
dates AS
(
SELECT q.n * 100 + qq.n AS ndate
FROM q, qq
)
SELECT COUNT(userid) as numlogins,
COUNT(DISTINCT userid) as numusers,
CAST('2000-01-01' + ndate AS DATETIME) as date
FROM dates
LEFT JOIN
usagelog
ON entryts >= CAST('2000-01-01' AS DATETIME) + ndate
AND entryts < CAST('2000-01-01' AS DATETIME) + ndate + 1
GROUP BY
ndate
This will select up to 10,000 dates constructed on the fly, that should be enough for 30 years.
SQL Server has a limitation of 100 recursions per CTE, that's why the inner queries can return up to 100 rows each.
If you need more than 10,000, just add a third CTE qqq(n) and cross-join with it in dates.