SQL, join workdate to pay rate by effective date - sql

I have 2 tables, one with employee work dates and one with pay rates for each employee and when those rates took effect.
They look like this:
CREATE TABLE pay_rates
(
[EMP ID] [int] NOT NULL,
[RATE] [decimal](11, 6) NOT NULL,
[EFFECTIVE DATE] [datetime] NOT NULL
)
CREATE TABLE work_dates
(
[EMP ID] [int] NOT NULL,
[WORK DATE] [datetime] NOT NULL,
)
The pay_rates table will have many entries for each employee as their pay was adjusted over the years.
The work_dates table contains dates each employee worked.
Both of these tables contain other information, but I've simplified down to the relevant columns.
I would like to join the two such that I can see the what the pay rate would have been on each work date. More technically I want to join each record in work_dates to a record in pay_rates where with the largest EFFECTIVE DATE that is less than the WORK DATE and where the EMP ID matches.

Perhaps when you join pay_rates to work_dates, you could use not exists to require that no rate exists whose effective date is on or before the work date and is strictly after the effective date of the rate to which you're joining. Something like this:
declare #pay_rates table (
[EMP ID] [int] NOT NULL,
[RATE] [decimal](11, 6) NOT NULL,
[EFFECTIVE DATE] [datetime] NOT NULL
);
declare #work_dates table (
[EMP ID] [int] NOT NULL,
[WORK DATE] [datetime] NOT NULL
);
insert #pay_rates values
(1, 10.25, '20160615'),
(1, 10.65, '20170101'),
(1, 11.85, '20180101');
insert #work_dates values
(1, '20160701'),
(1, '20170101'),
(1, '20170701'),
(1, '20171231'),
(1, '20180102');
select
WorkDate.[EMP ID],
WorkDate.[WORK DATE],
Rate.RATE,
Rate.[EFFECTIVE DATE]
from
#work_dates WorkDate
inner join #pay_rates Rate on
WorkDate.[EMP ID] = Rate.[EMP ID] and
WorkDate.[WORK DATE] >= Rate.[EFFECTIVE DATE] and
not exists
(
select 1
from #pay_rates LaterRate
where
LaterRate.[EMP ID] = WorkDate.[EMP ID] and
LaterRate.[EFFECTIVE DATE] <= WorkDate.[WORK DATE] and
LaterRate.[EFFECTIVE DATE] > Rate.[EFFECTIVE DATE]
);
I have assumed here that you do not allow two pay rates to have taken effect on the same date for the same employee. In that case, the above query would give you two results for each work date that uses those rates, since it has no way to prioritize one over the other.

I think you need:
SELECT pr2.[emp id], pr2.rate
FROM pay_rates pr2
INNER JOIN
(SELECT wd.[emp id], MAX(pr.[effective date]) AS mx_date
FROM work_dates wd
LEFT JOIN pay_rates pr
ON (wd.[emp id] = pr.[emp id] AND wd.[work date] > pr.[effective date])
GROUP BY wd.[emp id]) sub
ON pr2.[emp id] = sub.[emp id] AND pr2.[effective date] = sub.mx_date
In the sub-query, select the largest effective day per emp id that is less than the employee's work date. Then join the result back to the pay rates table to pull in the rate.

Related

Failed to convert NVARCHAR to INT, but I'm not trying to

I get this error:
Msg 245, Level 16, State 1, Line 5
Conversion failed when converting the nvarchar value 'L NOVAK ENTERPRISES, INC' to data type int.
I've been wrestling with this query for quite a while and just can't figure out in what place that the conversion is being attempted. Using SQL Server 2017.
DECLARE #StartDate AS DateTime
DECLARE #MfgGroupCode AS Varchar(20)
SET #StartDate='2/27/2020'
SET #MfgGroupCode = 'VOLVO_NLA'
SELECT DISTCINT
CT.No_ AS [Contact Number],
CT.Name AS [Contact Name],
UAL.Time AS [Search Date],
UAL.Param1 AS [Search Part],
CT.[E-Mail] AS [Contact Email],
CT.[Phone No_] AS [Contact Phone],
CT.[Company Name] AS [Search By Customer],
(SELECT C.Name
FROM dbo.[Customer] C
WHERE C.No_ = SL.[Sell-to Customer No_]
AND C.Name <> '') AS [Sold To Customer],
SL.[Posting Date] AS [Invoice Date],
SL.[Document No_] AS [Invoice],
SL.Quantity AS [Quantity],
SL.[Unit Price] AS [Unit Price],
SL.Amount AS [Amount],
DATEDIFF(DAY, UAL.Time, SL.[Posting Date]) AS [Interval]
FROM
dbo.[User Action Log] UAL
JOIN
dbo.[User Action Types] UAT ON UAL.[User Action ID] = UAT.ID
JOIN
dbo.[Item] I ON UAL.Param1 = I.[OEM Part Number]
JOIN
dbo.[Contact] CT ON UAL.[Contact No_] = CT.No_
LEFT OUTER JOIN
dbo.[Sales Invoice Line] SL ON UAL.Param1 = SL.[OEM Part Number]
AND SL.[Posting Date] >= #StartDate
WHERE
UAT.Name IN ('SinglePartSearch', 'MultiPartSearch')
AND UAL.[MFG Group Code] = #MfgGroupCode
AND UAL.Time >= #StartDate
AND UAL.Param3 > 0
-- AND DATEDIFF(DAY, UAL.Time, SL.[Posting Date]) < 0 -- Uncomment to see Current Searches with Past Orders
-- AND DATEDIFF(DAY, UAL.Time, SL.[Posting Date]) > -1 -- Uncomment to see Searches resulting in Future Order
AND DATEDIFF(DAY, UAL.Time, SL.[Posting Date]) IS NULL -- Uncomment to See Searches with no Order
ORDER BY
Interval DESC
Thanks to all of your help and questioning, I was able to identify that the culprit is UAL.Param3.
The User Action Log table stores a variety of different "Actions" and the parameters that are affiliated with each type of action (param1, param2, param3). For one action "RequestForAccess", the "L NOVAK" value is perfectly acceptable. For this query, we're looking at the "SinglePartSearch" and "MultiPartSearch" actions, which will only contain numeric values in UAL.Param3
I replaced this line (AND UAL.Param3 > 0) with (AND ISNUMERIC(UAL.Param3) = 1 AND UAL.Param3 <> 0) in the Where clause and it is now returning the results I hoped for.
Let me know if there is a more correct way of doing this and thank you to all who contributed!

Retrieve customers with more than one loan

I'm struggling to write a SQL query that will identify customers who have taken out multiple loans in a short period of time (say 60 days).
MyTable
CREATE TABLE [dbo].[MyTable](
[Customerid] [numeric](18, 0) NOT NULL,
[Date Open] [date] NOT NULL,
[Date Closed] [date] NOT NULL,
[Rank] [varchar](50) NOT NULL
) ON [PRIMARY]
E.g. - Customerid 1 has taken multiple loans with date open say 12/01/2017, 12/02/2017, 13/04/2018 etc. I need to find the customers who have taken next loan (Date Open) within 60 days.
It looks like you can do this with a simple INNER JOIN. I'm not entirely sure what Rank indicates in your table, but something like below should work if they can't take out two loans on the same day.
SELECT DISTINCT yt1.Customerid
FROM yourtable yt1
INNER JOIN yourtable yt2 ON yt2.Customerid = yt1.Customerid AND
yt2.[Date Open] > yt1.[Date Open] AND
yt2.[Date Open] <= DATEADD(DAY, 60, yt1.[Date Open])

How to return current and previous row in SQL?

How do I return the current row and the previous row in a SQL query that is organized by date on a join? I have to join the "Crew" table with its "Detail" table. When I execute the query using a subselect, I get the same data for each crew. I am not sure how to pass the current crew ID down to the subselect so that only the previous days work for the current crew is returned.
SELECT Spread_Crew.Description
, Sum(Abs(Daily_Progress.Station_Number_
Begin_Daily_Progress.Station_Number_End)) AS [Feet Total]
, Spread_Crew.Hourly_Employee_Count AS [Hourly]
, Spread_Crew.Salary_Employee_Count AS [Salary]
, (Spread_Crew.Hourly_Employee_Count +
Spread_Crew.Salary_Employee_Count)*10 AS [Weekly Hours]
, (Date() - Spread_Crew.Actual_Start_Date) AS [Crew Days to Date]
, Round(([Feet Total]/ [Crew Days to Date]),0) AS [FT/Day]
, (SELECT Sum(Abs(Daily_Progress.Station_Number_Begin
Daily_Progress.Station_Number_End))
FROM Spread_Crew INNER JOIN Daily_Progress ON Spread_Crew.ID =
Daily_Progress.Spread_Crew_Id
WHERE (((Daily_Progress.PROGRESS_DATE)=[Report Date]))
) AS [Previous Footage]
FROM Spread_Crew LEFT JOIN Daily_Progress ON Spread_Crew.ID =
Daily_Progress.Spread_Crew_Id
GROUP BY Spread_Crew.Description
, Spread_Crew.Hourly_Employee_Count
, Spread_Crew.Salary_Employee_Count
, Spread_Crew.Sort_Order
, Spread_Crew.Print_On_Daily_Report
, Spread_Crew.Actual_Start_Date
HAVING (((Spread_Crew.Print_On_Daily_Report)=True))
ORDER BY Spread_Crew.Sort_Order;
Your subquery probably wants to be a correlated subquery:
(SELECT Sum(Abs(dp.Station_Number_Begin - dp.Station_Number_End))
FROM Daily_Progress as dp
WHERE Spread_Crew.ID = dp.Spread_Crew_Id AND
dp.PROGRESS_DATE = [Report Date]
) AS [Previous Footage]
The expression Spread_Crew.ID refers to the column in the outer SELECT.

Column invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause

We have a table which will capture the swipe record of each employee. I am trying to write a query to fetch the list of distinct employee record by the first swipe for today.
We are saving the swipe date info in datetime column. Here is my query its throwing exception.
select distinct
[employee number], [Employee First Name]
,[Employee Last Name]
,min([DateTime])
,[Card Number]
,[Reader Name]
,[Status]
,[Location]
from
[Interface].[dbo].[VwEmpSwipeDetail]
group by
[employee number]
where
[datetime] = CURDATE();
Getting error:
Column 'Interface.dbo.VwEmpSwipeDetail.Employee First Name' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
Any help please?
Thanks in advance.
The error says it all:
...Employee First Name' is invalid in the select list because it is not contained
in either an aggregate function or the GROUP BY clause
Saying that, there are other columns that need attention too.
Either reduce the columns returned to only those needed or include the columns in your GROUP BY clause or add aggregate functions (MIN/MAX). Also, your WHERE clause should be placed before the GROUP BY.
Try:
select distinct [employee number]
,[Employee First Name]
,[Employee Last Name]
,min([DateTime])
,[Card Number]
,min([Reader Name])
from [Interface].[dbo].[VwEmpSwipeDetail]
where CAST([datetime] AS DATE)=CAST(GETDATE() AS DATE)
group by [employee number], [Employee First Name], [Employee Last Name], [Card Number]
I've removed status and location as this is likely to return non-distinct values. In order to return this data, you may need a subquery (or CTE) that first gets the unique IDs of the SwipeDetails table, and from this list you can join on to the other data, something like:
SELECT [employee number],[Employee First Name],[Employee Last Name].. -- other columns
FROM [YOUR_TABLE]
WHERE SwipeDetailID IN (SELECT MIN(SwipeDetailsId) as SwipeId
FROM SwipeDetailTable
WHERE CAST([datetime] AS DATE)=CAST(GETDATE() AS DATE)
GROUP BY [employee number])
Please Try Below Query :
select distinct [employee number],[Employee First Name]
,[Employee Last Name]
,min([DateTime])
,[Card Number]
,[Reader Name]
,[Status]
,[Location] from [Interface].[dbo].[VwEmpSwipeDetail] group by [employee number],[Employee First Name]
,[Employee Last Name]
,[Card Number]
,[Reader Name]
,[Status]
,[Location] having [datetime]=GetDate();
First find the first timestamp for each employee on the given day (CURDATE), then join back to the main table to get all the details:
WITH x AS (
SELECT [employee number], MIN([datetime] AS minDate
FROM [Interface].[dbo].[VwEmpSwipeDetail]
WHERE CAST([datetime] AS DATE) = CURDATE()
GROUP BY [employee number]
)
select [employee number]
,[Employee First Name]
,[Employee Last Name]
,[DateTime]
,[Card Number]
,[Reader Name]
,[Status]
,[Location]
from [Interface].[dbo].[VwEmpSwipeDetail] y
JOIN x ON (x.[employee number] = y.[employee number] AND x.[minDate] =Y.[datetime]
This should not be marked as mysql as this would not happen in mysql.
sql-server does not know which of the grouped [Employee First Name] values to return so you need to add an aggregate (even if you only actually expect one result). min/max will both work in that case. The same would apply to all the other rows where they are not in the GROUP BY or have an aggregate function (EG min) around them.

Access 2010 -- query to determine inclusion in date range

I am a newbie to both SQL and Access and was wondering if you could help please?
I am in the process of creating a hotel bookings database using Access 2010 but I cannot get my query working where I search for a vacant room.
My database has 5 tables as follows (Field Names in brackets):
BOOKINGS (BookRef, CustAcctNo, BookDate, ArrivDate, DurStay, EmpNo, RoomNo)
CUSTOMERS (CustAcctNo, Title, Forename, Surname, Address1, Address2, Address3)
EMPLOYEES (EmpNo, Title, Forename, Surname)
ROOM TYPES (RoomType, Description, Rate/Price)
ROOMS (RoomNo, RoomType)
These tables all have a 'one-to-many' relationship i.e. one customer can have many bookings.
So, my thinking is that the Fields of interest would be the ArrivDate Field (date of arrival) and DurStay (Duration of Stay) Field. In the Rooms table the Room Number is the Field I am calling out.
So, the closest I have got so far is the following:
PARAMETERS [Start Date] DateTime, [End Date] DateTime;
SELECT R.*, [Start Date] AS Expr1, [End Date] AS Expr2, *
FROM ROOMS AS R
LEFT JOIN (SELECT B.RoomNo
FROM Bookings AS B
WHERE ([Start Date] between B.ArrivDate and (B.ArrivDate + [Please Enter]))
AND ([End Date] between B.ArrivDate and (B.ArrivDate + B.DURSTAY))) AS BKD
ON R.RoomNo = BKD.RoomNo
WHERE (((BKD.RoomNo) Is Null));
This just doesn't seem to be working for me at all. I have tried many times with different versions of the above code but seem to be getting nowhere. My thoughts were that I do a search where the field is null between the dates plus the duration of stay but maybe I am going about it the wrong way I am not sure.
Hopefully I have provided enough detail here but please let me know if you need to know more. I really appreciate you all having a look at this at least. Maybe a fresh outlook on it might spot where I am going wrong.
Many thanks in advance for any help you can offer.
Try changing from this:
WHERE ([Start Date] between B.ArrivDate and ...
AND ([End Date] between B.ArrivDate and ...
to this:
WHERE ([Start Date] between B.ArrivDate and ...
OR ([End Date] between B.ArrivDate and ...
I think this fulfills the logic better because it accepts a partial match, that is, an overlap in dates.
PARAMETERS [Start Date] DateTime, [End Date] DateTime;
SELECT R.*, [Start Date] AS Expr1, [End Date] AS Expr2, *
FROM ROOMS AS R
LEFT JOIN (SELECT B.RoomNo
FROM Bookings AS B
WHERE ([Start Date] between B.ArrivDate and (B.ArrivDate + **B.DURSTAY**))
**OR** ([End Date] between B.ArrivDate and (B.ArrivDate + B.DURSTAY))) AS BKD
ON R.RoomNo = BKD.RoomNo
WHERE (((BKD.RoomNo) Is Null));
or
an easier, more elegant query would be something like:
SELECT R.*, #06/05/2014# as [Start Date] , #06/10/2014# as [End Date]
FROM ROOMS AS R
WHERE R.RoomNo not in
(
SELECT DISTINCT RoomNo FROM Bookings B
WHERE (#06/05/2014# between B.Arrival and (B.Arrival + B.Duration))
OR (#06/10/2014# between B.Arrival and (B.Arrival + B.Duration))
);
The date parameters must be passed in the american format (#MM/DD/YYYY#)