Date Comparison of Two Tables in SQL SERVER - sql-server

I had this Data,
Table One :
EmpID Date Absent
1 01/01/2018 1
1 01/02/2018 1
1 02/05/2018 1
1 03/25/2018 1
1 04/01/2018 0
1 05/02/2018 1
1 06/03/2018 1
Table Two
ID Amount DateEffective
1 5.00 02/06/2018
2 3.00 05/02/2018
3 10.00 06/03/2018
Desired Output
EmpID Month Year Absent Penalty
1 January 2018 2 5.00
1 February 2018 1 5.00
1 March 2018 1 3.00
1 April 2018 0 3.00
1 May 2018 1 13.00
1 June 2018 1 10.00
This is my Code
SELECT { fn MONTHNAME(one.Date) } AS MonthName, YEAR(one.Date) AS Year, SUM(one.Absent) AS Absent,
(
SELECT top 1 two.DailyRate
FROM table_two as two
WHERE EmpID = '1'
AND one.Date <= two.EffectivityDate
)
FROM table_one as one
WHERE EmpID = '1'
GROUP BY { fn MONTHNAME(one.Date) }, MONTH(one.Date), YEAR(one.DTRDate)
ORDER BY Year(one.Date),month(one.Date)
and it shows an error :
Column 'one.Date' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause
please help for this issue...
Thanks

Try this :
SELECT
one.EmpID
,DATENAME(MONTH,one.Date) AS [MonthName]
,YEAR(one.Date) AS [Year]
,SUM(one.Absent) AS [Absent]
,(SELECT top 1 two.Amount
FROM table_two as two
WHERE two.ID = one.EmpID
AND YEAR(two.DateEffective) >= YEAR(one.Date)
AND MONTH(two.DateEffective) >=MONTH(one.Date)
) AS [Penalty]
FROM table_one as one
WHERE
one.EmpID = '1'
GROUP BY one.EmpID,DATENAME(MONTH,one.Date), MONTH(one.Date), YEAR(one.Date)
ORDER BY Year(one.Date),month(one.Date)

From my understanding to do this,
select e.EmpID
,datename(month,e.Date)[month]
,year(e.Date) [year]
,sum(e.Absent) as [Abscount]
,a.Amount
from
empl e left join abs a
on datename(month,e.Date)=DATENAME(month,a.DateEffective)
group by e.EmpID,DATENAME(MONTH,e.Date), MONTH(e.Date), YEAR(e.Date) , a.Amount
order by Abscount desc
Revert me if any clarifications needed...

is this helpful.?
Create Table #TabOne(EmpID int,[Date] Date,[Absent] Bit)
Create Table #TabTwo(ID int,Amount float,DateEffective Date)
Insert into #TabOne
SELECT 1,'01/01/2018',1 Union All
SELECT 1,'01/02/2018',1 Union All
SELECT 1,'02/05/2018',1 Union All
SELECT 1,'03/25/2018',1 Union All
SELECT 1,'04/01/2018',0 Union All
SELECT 1,'05/02/2018',1 Union All
SELECT 1,'06/03/2018',1
Insert into #TabTwo
Select 1,5.00 ,'02/06/2018' Union All
Select 2,3.00 ,'05/02/2018' Union All
Select 3,10.00,'06/03/2018'
;with cte1
As
(
Select One.EmpID,MONTH(one.[Date]) As [mon],YEAR(one.[Date]) As [Year],two.Amount,one.[Absent],
ROW_NUMBER() OVER(partition by One.EmpID,One.[Date] order by DATEDIFF(dd,two.DateEffective,one.[Date]) desc) as rn
from #TabOne one
LEFT JOIN #TabTwo two on one.[Date]<=two.DateEffective
)
Select EmpID,DATENAME(month, DATEADD(month, [mon]-1, CAST('2008-01-01' AS datetime))) As [Month],
[Year],SUM(CASE WHEN [Absent]=0 then 0 ELSE 1 END) As [Absent] ,MAX(Amount) As Penalty
from cte1
where rn=1
Group by EmpID,[Year],[mon]
order by EmpID,[Year],[mon]
Drop Table #TabOne
Drop Table #TabTwo

Related

How to get previous record if current record does not exist in table

my table like this
Id Date type quantity
1 29/04/2019 APPLE 2
2 29/04/2019 Banana 15
3 29/04/2019 Mango 100
4 29/04/2019 Grapes 50
5 29/04/2019 Fish 80
6 30/04/2019 APPLE 4
7 30/04/2019 Grapes 100
8 30/04/2019 Fish 90
9 01/05/2019 APPLE 6
10 01/05/2019 Banana 30
11 01/05/2019 Grapes 150
12 01/05/2019 Fish 100
13 02/05/2019 Mango 200
14 02/05/2019 Grapes 200
15 02/05/2019 Fish 110
16 03/05/2019 APPLE 8
17 03/05/2019 Banana 45
18 03/05/2019 Mango 300
19 04/05/2019 APPLE 10
20 04/05/2019 Grapes 300
21 04/05/2019 Fish 120
22 05/05/2019 APPLE 12
23 05/05/2019 Fish 130
i miss some inputs every day,But i need to fill the gaps with previous row of the same "Type" on 30/04/2019 i missed "Banana & Mango" bu i need like
Id Date type quantity
1 29/04/2019 APPLE 2
2 29/04/2019 Banana 15
3 29/04/2019 Mango 100
4 29/04/2019 Grapes 50
5 29/04/2019 Fish 80
6 30/04/2019 APPLE 4
7 30/04/2019 Grapes 100
8 30/04/2019 Fish 90
9 30/04/2019 Banana 15
10 30/04/2019 Mango 100
actually last two rows are null but it should updated same on 29/04/2019
I think the easiets way might be this:
DECLARE #PDate DATE = SELECT TOP 1 Date FROM YourTable ORDER BY Date ASC --Previous Date
DECLARE #NDate DATE = SELECT TOP 1 Date FROM YourTable WHERE DATE>#PDate --Next Date
WHILE (#NDate IS NOT NULL)
BEGIN
WITH X AS
(
SELECT T1.Date AS Date1, T1.Type AS Type1, T1.Quantity AS Q1
T2.Date AS Date2, T2.Type AS Type2, T2.Quantity AS Q2
FROM YourTable T1
LEFT JOIN YourTable T2 ON T1.Type = T2.Type
WHERE T1.Date = #PDate AND T2.Date = #NDate
)
INSERT INTO YourTable (Date,Type,Quantity)
SELECT #NDate,Type1,Q1
WHERE X.Type2 IS NULL
SET #PDate = #NDate
SET #NDate = NULL -- If next result wasnt found this stays null for while condition
SET #NDate = SELECT TOP 1 Date FROM YourTable WHERE Date>#PDate
END
I think this is the way that may work and I wish so
( if there is any syntax or ... mistakes its because I didnt have SSMS installed to test. Sorry)
try this :
declare #date date
and for initiate #date you can use select #date=max(date) from table1 or pass static value set #date='02/01/2019'
and then find input
select input,max(date) as MaxDate into #temp
from table1
where input not in (select input from table1 where date=#date )
group by input
then :
select t.* from Table1 t join #temp on Table1.input=#temp.Input and Table1.date=#temp.MaxDate
OK, after the goal posts are settled, this is one method. Note that this solution builds both a Types and Dates dataset. Really the Types dataset should already exist somewhere in your database, and you should create a Calendar Table if you're going to be doing this type of work often.
Any way, I've left comments in the code for you. I've assumed you're using SQL Server 2012+, as 2008 is literally about to run out of support.
CREATE TABLE dbo.MyTable (ID int IDENTITY(1,1),
[date] date,
[type] varchar(10),
Quantity int);
INSERT INTO dbo.MyTable
SELECT CONVERT(date,[date],103),
RTRIM([Type]),
Quantity
FROM (VALUES('29/04/2019','APPLE ',2),
('29/04/2019','Banana',15),
('29/04/2019','Mango ',100),
('29/04/2019','Grapes',50),
('29/04/2019','Fish ',80),
('30/04/2019','APPLE ',4),
('30/04/2019','Grapes',100),
('30/04/2019','Fish ',90),
('01/05/2019','APPLE ',6),
('01/05/2019','Banana',30),
('01/05/2019','Grapes',150),
('01/05/2019','Fish ',100),
('02/05/2019','Mango ',200),
('02/05/2019','Grapes',200),
('02/05/2019','Fish ',110),
('03/05/2019','APPLE ',8),
('03/05/2019','Banana',45),
('03/05/2019','Mango ',300),
('04/05/2019','APPLE ',10),
('04/05/2019','Grapes',300),
('04/05/2019','Fish ',120),
('05/05/2019','APPLE ',12),
('05/05/2019','Fish ',130)) V([date],[Type],Quantity);
GO
--SELECT *
--FROM dbo.MyTable;
GO
--Create a calendar table
WITH N AS (
SELECT N
FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL)) N(N)),
Tally AS(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) -1 AS I
FROM N N1, N N2, N N3), --1000 days shuld be enough
Dates AS(
SELECT DATEADD(DAY, T.I, MIN(MT.[date])) AS [Date]
FROM Tally T
CROSS JOIN dbo.MyTable MT
GROUP BY T.I
HAVING DATEADD(DAY, T.I, MIN(MT.[date])) <= MAX([Date])),
--Get Types
Types AS (
SELECT DISTINCT [Type]
FROM dbo.MyTable MT),
--Create islands
Grps AS(
SELECT MT.ID,
D.[Date],
T.[Type],
MT.Quantity,
COUNT(MT.Quantity) OVER (PARTITION BY T.[Type] ORDER BY D.[date]
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS Grp
FROM Dates D
CROSS JOIN Types T
LEFT JOIN dbo.MyTable MT ON D.[Date] = MT.[date]
AND T.[type] = MT.[type])
SELECT G.ID AS ID,
ROW_NUMBER() OVER (ORDER BY G.[Date], G.[Type]) AS RN,
G.[Date],
G.[Type],
MAX(G.Quantity) OVER (PARTITION BY G.[Type], G.Grp) AS Quantity
FROM Grps G
ORDER BY G.[Date],
G.[Type];
GO
DROP TABLE dbo.MyTable;
db<>fiddle
I think using cursor is a good option to insert your missing entries in the table. By cursor you will be able to check date wise missing types and insert them with the previous quantity.
You can also use this following script to find the missing records in your table. To create the script I consider the table name = 'add_missing_records'
SELECT AA.date AS [Date],
AA.type AS [Type],
BB.quantity AS [Original Quantity] ,
CASE
WHEN BB.quantity IS NULL THEN
(
SELECT quantity
FROM add_missing_records C
WHERE C.date = (
SELECT MAX([date])
FROM add_missing_records B
WHERE B.date < AA.date
AND B.type = AA.type
)
AND C.type = AA.type
)
ELSE BB.quantity
END AS [New Quantuty]
FROM (
SELECT date,type
FROM (
SELECT DISTINCT 'A' AS common,date
FROM add_missing_records
)A
FULL JOIN (
SELECT DISTINCT 'A' as common, type
FROM add_missing_records
)B
ON a.common = b.common
) AA
LEFT JOIN add_missing_records BB
ON AA.date = BB.date
AND AA.type = BB.type
WHERE BB.quantity IS NULL
ORDER BY 1,2

Union rows to columns in SQL server

This is the table "tbltask",
task Ship_Operator Pick_Operator Pack_Operator
1 john kevin steve
2 kevin kevin john
3 steve john john
4 steve steve steve
5 john steve john
Now I need to get the total amount for everyone: ship, pick, pack
name ship Total pick Total pack Total
john 2 1 3
kevin 1 2 0
steve 2 2 2
I can use three statement to get the result:
select [Ship_Operator] ,count(*) as task_total from tbltask
where [Ship_Operator] in ('john','kevin','steve')
group by [Ship_Operator]
select [Pick_Operator] ,count(*) as task_total from tbltask
where [Pick_Operator] in ('john','kevin','steve')
group by [Pick_Operator]
select [Pack_Operator] ,count(*) as task_total from tbltask
where [Pack_Operator] in ('john','kevin','steve')
group by [Pack_Operator]
Is it possible to use one SQL statement?
Your help will be appreciated!
unpivot and pivot back:
with t(task,Ship_Operator,Pick_Operator,Pack_Operator) as (
select '1','john','kevin','steve' union all
select '2','kevin','kevin','john' union all
select '3','steve','john','john' union all
select '4','steve','steve','steve' union all
select '5','john','steve','john')
-------Test data set up ends here------------
select
name, Ship_Operator ship_total, Pick_Operator pick_total, Pack_Operator pack_total
from t unpivot (
name for operation in (Ship_Operator, Pick_operator, Pack_Operator)
) as x pivot (
count(task) for operation in ([Ship_Operator],[Pick_Operator],[Pack_Operator])
) as x;
Produces:
Another way is to use UNPIVOT only and then conditionally aggregate:
with t(task,Ship_Operator,Pick_Operator,Pack_Operator) as (
select '1','john','kevin','steve' union all
select '2','kevin','kevin','john' union all
select '3','steve','john','john' union all
select '4','steve','steve','steve' union all
select '5','john','steve','john')
-------Test data set up ends here------------
select
name,
count(case when operation = 'Ship_Operator' then 1 end) ship,
count(case when operation = 'Pick_Operator' then 1 end) pick,
count(case when operation = 'Pack_Operator' then 1 end) pack
from t unpivot (
name for operation in (Ship_Operator, Pick_operator, Pack_Operator)
) as x
group by name;
Produces:
Use UNION to combine all name and add a new column to just identify the different values.
Query
SELECT t.[Name],
SUM(CASE t.[Col1] WHEN 'Ship' THEN 1 ELSE 0 END) AS [Ship Total],
SUM(CASE t.[Col1] WHEN 'Pick' THEN 1 ELSE 0 END) AS [Pick Total],
SUM(CASE t.[Col1] WHEN 'Pack' THEN 1 ELSE 0 END) AS [Pack Total]
FROM(
SELECT 'Ship' AS [col1], [Ship_operator] as [Name]
FROM [tbltask]
UNION ALL
SELECT 'Pick', [Pick_operator]
FROM [tbltask]
UNION ALL
SELECT 'Pack' AS [col1], [Pack_operator]
FROM [tbltask]
)t
GROUP BY t.[Name];
And if you want the result particularly for those three names. Then add a WHERE condition in the sub-queries.

Update table with overlap date range and change status

I have a table with following column and I would like to update it as following.
The Logic is the start date take the date will be updated if overlap with following rules: take the earliest start date and enddate of the latest row with overlapping date based on member id. And the status of the remaining overlap column will be updated to 2. Hope someone could help.
ID MemberID StartDate EndDate Status
1 2 2015-01-01 2015-02-28 1
2 2 2015-02-01 2015-02-03 1
3 2 2015-02-01 2015-03-01 1
4 1 2015-02-01 2015-02-28 1
5 3 2015-02-01 2015-02-28 1
6 2 2015-05-01 2015-05-20 1
I would like to update to
ID MemberID StartDate EndDate Status
1 2 2015-01-01 2015-03-01 1
2 2 2015-01-01 2015-03-01 2
3 2 2015-01-01 2015-03-01 2
4 1 2015-02-01 2015-02-28 1
5 3 2015-02-01 2015-02-28 1
6 2 2015-05-01 2015-05-20 1
I think this should do it :
update a set
a.startdate =
(select min(startdate) from #table where memberID = a.memberID),
a.enddate =
(select max(enddate) from #table where memberID = a.memberID),
a.status =
case when a.id =
(select min(id) from #table where memberID = a.memberID)
then status else 2
end
from #table a
Try this,
---- Creating CTE for finding overlapped dates
;WITH CTE AS (
SELECT A.ID,
B.ID AS MAPPED_ID,
A.MEMBERID,
B.STARTDATE,
B.ENDDATE,
B.STATUS
FROM #YOUR_TABLE A
JOIN #YOUR_TABLE B ON B.STARTDATE <= A.ENDDATE-- Condition for finding the overlapped dates
AND B.ENDDATE >= A.STARTDATE
AND A.MEMBERID = B.MEMBERID)-- end here
UPDATE T
SET T.STARTDATE = A.STARTDATE,
T.ENDDATE = A.ENDDATE,
T.STATUS = A.STATUS
FROM #YOUR_TABLE T
JOIN (SELECT ID,
MEMBERID,
STARTDATE,
ENDDATE,
STATUS=CASE
WHEN RN > 1 THEN 2
ELSE 1
END
FROM (SELECT T.ID,
T.MEMBERID,
CS1.STARTDATE,
CS2.ENDDATE,
ROW_NUMBER() -- ROWNUMBER FOR FINDING THE STATUS
OVER(
PARTITION BY T.MEMBERID, CS1.STARTDATE, CS2.ENDDATE
ORDER BY T.ID) AS RN
FROM #YOUR_TABLE T
CROSS APPLY (SELECT CAST(MIN(STARTDATE)AS DATETIME) AS STARTDATE --- FINDING MIN(STARTDATE) FOR THE OVERLAPPED GROUP
FROM CTE A
WHERE A.ID = T.ID) CS1
CROSS APPLY (SELECT ENDDATE -- FINDING LAST ENDDATE FOR THE OVERLAPPED GROUP (IE RN=1)
FROM (SELECT ENDDATE,--- ROW_NUMBER FOR THE OVERLAPPED GROUPS
ROW_NUMBER()
OVER(
ORDER BY B.MAPPED_ID DESC) AS RN
FROM CTE B
WHERE B.ID = T.ID)A
WHERE A.RN = 1)CS2)A)A ON A.ID = T.ID
SELECT *
FROM #YOUR_TABLE

How to get number of employees per year

I have a table called Mst_Employee. The fields are:
Emp_No | Emp_Name | Emp_JoiningDate | Emp_ResignedDate | Emp_Status
How do I get the No. of Employees by year for each year somebody joined or resigned? (Joined and Resigned includes by year)
E.g. result should look like this:
Year No. of Employees.
------------------------
2011 125
2012 130
2013 100
One way to solve it is with a recursive cte and group by:
DECLARE #FromYear int, #ToYear int
SELECT #FromYear = YEAR(MIN(Emp_JoiningDate)),
#ToYear = YEAR(GETDATE())
FROM Mst_Employee
;WITH CTE AS
(
SELECT #FromYear As TheYear
UNION ALL
SELECT TheYear + 1
FROM CTE
WHERE TheYear < #ToYear
)
SELECT TheYear as [Year],
COUNT
(
CASE WHEN TheYear <= YEAR(COALESCE(Emp_ResignedDate, GETDATE())) THEN
1
END
) As [No. of Employees.]
FROM CTE
INNER JOIN Mst_Employee ON(TheYear >= YEAR(Emp_JoiningDate))
GROUP BY TheYear
See fiddle here
You can achieve this with:
select y as [Year], count(*) as [No. of Employees.]
from(select Emp_No, YEAR(Emp_JoiningDate) as y from Mst_Employee
union
select Emp_No, YEAR(Emp_ResignedDate) from Mst_Employee
where Emp_ResignedDate is not null)t
group by y

Select element show up in the next 2 month. SQL SERVER

This is a result of a SELECT query of mine.
Code Name MONTH
------- ------------ -----------
Cust3 CustName3 1
Cust2 CustName2 2
Cust5 CustName5 3
Cust2 CustName2 5
Cust2 CustName2 6
Cust3 CustName3 7
Cust6 CustName6 8
Cust1 CustName1 10
Cust1 CustName1 11
Cust3 CustName3 12
Now I want to select rows that have the same Code value for two consecutive months, for example Cust2 is contiguous in months 5 and 6 and Cust1 is contiguous in months 10 and 11:
Code Name MONTH
------- ------------ -----------
Cust2 CustName2 5
Cust2 CustName2 6
Cust1 CustName1 10
Cust1 CustName1 11
Here is another approach using ROW_NUMBER and COUNT() OVER:
SQL Fiddle
WITH Cte AS(
SELECT *,
RN = [Month] - ROW_NUMBER() OVER(PARTITION BY Code ORDER BY [Month])
FROM TestData
),
Cte2 AS(
SELECT *,
CC = COUNT(*) OVER(PARTITION BY Code, RN)
FROM Cte
)
SELECT
Code, Name, [Month]
FROM Cte2
WHERE CC >= 2
I assume the underlying table is denormalized and has this schema:
Code varchar(n),
Name nvarchar(n),
Month int
...and that there is only 1 row for each month.
We can use the LAG function in T-SQL to compare neighbour rows. We don't need the optional OVER partition-by clause as it's a single data set group:
SELECT
[Code],
[Month]
FROM
(
SELECT
[Code],
LAG( [Code], 1 ) AS LastCode
[Month],
LAG( [Month], 1 ) AS LastMonth
FROM
[sourceTable]
ORDER BY
[Month] ASC
) AS Lagged
WHERE
Lagged.[Code] = Lagged.LastCode
AND
Lagged.[Month] = Lagged.LastMonth
The above two answers are pretty neat. Here's another alternative way of solving this (example), perhaps not as elegantly:
select * from
(
-- join to itself, but for month - join to previous month
select a.code, a.month
from test a
inner join test b on a.code=b.code and a.month = b.month-1
union all
-- join to itself, but for month - join to next month
select a.code, a.month
from test a
inner join test b on a.code=b.code and a.month = b.month+1
) a
order by a.code, a.month

Resources