Sql Server Insert values into table based on existing rows - sql-server

I have the tables tbl1 and tbl2, as shown below:
tbl1:
ID Date1 Date2
1 2020-05-11 2020-09-14
1 2020-06-13 2020-09-14
1 2020-07-15 2020-09-14
2 2020-10-01 2020-09-14
tbl1:
ID Date1 Date2 ID2
1 2020-05-11 NULL 15
1 2020-06-13 2020-10-01 15
1 2020-04-10 NULL 15
I need to insert rows into tbl2 from tbl1 if the ID and Date1 don't already exist in tbl2. My output needs to look like:
tbl2:
ID Date1 Date2 ID2
1 2020-05-11 NULL 15
1 2020-06-13 2020-10-01 15
1 2020-04-10 NULL 15
1 2020-07-15 2020-09-14 15
2 2020-10-01 2020-09-14 15
I have tried using except operator, but it doesn't work. What's the best way to do this?

Use not exists:
insert into tbl2 (id, date1, date2, id2)
select t1.id, t1.date1, t1.date2, 15
from tbl1 t1
where not exists (select 1 from tbl2 t2 where t2.id = t1.id and t2.date1 = t1.date1)
I am unclear about what you want with id2, so the above query hardcodes value 15. Maybe you want something dynamic based on available values in the target table - if so:
insert into tbl2 (id, date1, date2, id2)
select t1.id, t1.date1, t1.date2, ti.id2
from tbl1 t1
cross join (select distinct id2 from tbl1) ti
where not exists (select 1 from tbl2 t2 where t2.id = t1.id and t2.date1 = t1.date1 and t2.id2 = ti.id2)

Related

left join and get the maximum datetime value

I have two tables ,
Table 1
Id Name
===========
1 Name1
2 Name2
3 Name3
Table 2
Id Tb1Id DateTime
=======================
1 1 20-09-2017
2 1 01-09-2018
3 2 01-09-2016
4 2 02-09-2015
5 3 06-09-2016
6 3 10-09-2019
I want to join those two tables by where Table1.Id = Table2.Tb1Id and get the maximum datetime value from Table2. The result should be like this .
Id Name DateTime
========================
1 Name1 01-09-2018
2 Name2 01-09-2016
3 Name3 10-09-2019
Try This
DECLARE #Table1 AS TABLE(Id INT,Name VARCHAR(20))
INSERT INTO #Table1
SELECT 1,'Name1' UNION ALL
SELECT 2,'Name2' UNION ALL
SELECT 3,'Name3'
DECLARE #Table2 AS TABLE(Id INT, Tb1Id INT,[DateTime] DATETIME)
INSERT INTO #Table2
SELECT 1,1,'2017-09-20' UNION ALL
SELECT 2,1,'2018-09-01' UNION ALL
SELECT 3,2,'2016-09-01' UNION ALL
SELECT 4,2,'2015-09-02' UNION ALL
SELECT 5,3,'2016-09-06' UNION ALL
SELECT 6,3,'2019-09-10'
SELECT t2.Tb1Id AS Id,
t1.Name,
MAX(t2.[DateTime]) AS [DateTime]
FROM #Table1 AS T1
INNER JOIN #Table2 AS t2
ON T1.Id = t2.Tb1Id
GROUP BY
t2.Tb1Id,
t1.Name
Result
Id Name DateTime
-----------------------------------
1 Name1 2018-09-01 00:00:00.000
2 Name2 2016-09-01 00:00:00.000
3 Name3 2019-09-10 00:00:00.000

Compare multiple dates on one table with multiple dates in another

I'm using T-SQL in SSMS 2016. For a report, I want to compare two tables to see if the date ranges in table2 cover the date ranges in table1, then return the rows in table1 that are not fully covered by the date ranges in table2.
The number of entries in table1 and table2 will grow in number over time.
table1 table2
id start date end_date id start date end date
----------------------------- ----------------------------
1001 01/08/17 31/08/17 1001 07/07/17 02/09/17
1001 01/10/17 31/10/17 1001 01/11/17 12/12/17
1001 01/11/17 30/11/17
1001 01/01/18 05/01/18
Question 1 Query Answer
-- Does Table2 cover the date ranges in table1? Result is by each date in Table2.
DECLARE #t1_minStartDate DATE,
#t1_maxEndDate DATE
SELECT #t1_minStartDate = MIN(startDate),
#t1_maxEndDate = MAX(endDate)
FROM table1
SELECT startDate AS t2_startDate,
CASE WHEN startDate < #t1_minStartDate THEN
'Yes'
ELSE
'No'
END AS t2_startDate_covers_all,
endDate AS t2_endDate,
CASE WHEN startDate > #t1_maxEndDate THEN
'Yes'
ELSE
'No'
END AS t2_endDate_covers_all
FROM table2 t2
Result:
t2_startDate t2_startDate_covers_all t2_endDate t2_endDate_covers_all
-------------------------------------------------------------------------
2017-07-07 Yes 2017-09-02 No
2017-11-01 No 2017-12-12 No
Question 2 Query Answer
-- Rows in Table1 that are not fully covered by the date ranges in table2
SELECT t1.id,
t1.startDate,
t1.endDate
FROM table1 t1
WHERE NOT EXISTS (SELECT 1
FROM table2 t2
WHERE t1.startDate BETWEEN t2.startDate AND t2.endDate
AND t1.endDate BETWEEN t2.startDate AND t2.endDate)
Result:
id startDate endDate
----------------------------------
2 2017-10-01 2017-10-31
4 2018-01-01 2018-01-05

If Value is present in two consecutive months , display only one month in sql

I would want to check ID in consecutive months, IF Same ID is present in two consecutive months then consider that ID only for 1st month.
If ID's are not in consecutive month then show the distinct ID's grouped by start date month.(We consider only start date)
For example, ID 1 is present in start date months january and Feb , then Distinct count of this ID will be 1 in Jan, how ever ID 2 and 3 are
present in Jan and March and Feb and May Resp, now I would like to see this distinct count of ID in Jan and March.
Current Data
Table1:
ID StartDate EndDate
1 2017-01-12 2017-01-28
1 2017-01-19 2017-01-28
1 2017-01-29 2017-02-11
1 2017-02-01 2017-02-11
1 2017-02-19 2017-02-24
2 2017-01-12 2017-01-28
2 2017-01-19 2017-01-28
2 2017-03-09 2017-03-20
3 2017-02-12 2017-02-28
3 2017-02-19 2017-02-28
3 2017-05-05 2017-05-29
3 2017-05-09 2017-05-29
I tried with below logic bt I know I am missing on something here.
select t.* from Table1 t
join Table1 t t1
on t1.ID=t.ID
and datepart(mm,t.StartDate)<> datepart(mm,t1.StartDate)+1
Expected Result:
DistinctCount StartDateMonth(In Numbers)
1 1(Jan)
2 1(Jan)
2 3(March)
3 2(Feb)
3 5(May)
Any help is appreciated!
Here's my solution. The thinking for this is:
1) Round all the dates to the first of the month, then work with the distinct dataset of (ID, StartDateRounded). From your dataset, the result should look like this:
ID StartDateRounded
1 2017-01-01
1 2017-02-01
2 2017-01-01
2 2017-03-01
3 2017-02-01
3 2017-05-01
2) From this consolidated dataset, find all records by ID that do not have a record for the previous month (which means it's not a consecutive month and thus is a beginning of a new data point). This is your final dataset
with DatesTable AS
(
SELECT DISTINCT ID
,DATEADD(month,DateDiff(month,0,StartDate),0) StartDateRounded
,DATEADD(month,DateDiff(month,0,StartDate)+1,0) StartDateRoundedPlusOne
FROM Table1
)
SELECT t1.ID, DatePart(month,t1.StartDateRounded) AS StartDateMonth
FROM DatesTable t1
LEFT JOIN DatesTable t2
ON t1.ID = t2.ID
AND t1.StartDateRounded = t2.StartDateRoundedPlusOne
WHERE t2.ID IS NULL; --Verify no record exists for prior month
sqlfiddler for reference. Let me know if this helps
Just need to take advantage of the lag on the inner query to compare values between rows, and apply the logic in question on the middle query, and then do a final select.
/*SAMPLE DATA*/
create table #table1
(
ID int not null
, StartDate date not null
, EndDate date null
)
insert into #table1
values (1, '2017-01-12', '2017-01-28')
, (1, '2017-01-19', '2017-01-28')
, (1, '2017-01-29', '2017-02-11')
, (1, '2017-02-01', '2017-02-11')
, (1, '2017-02-19', '2017-02-24')
, (2, '2017-01-12', '2017-01-28')
, (2, '2017-01-19', '2017-01-28')
, (2, '2017-03-09', '2017-03-20')
, (3, '2017-02-12', '2017-02-28')
, (3, '2017-02-19', '2017-02-28')
, (3, '2017-05-05', '2017-05-29')
, (3, '2017-05-09', '2017-05-29')
/*ANSWER*/
--Final Select
select c.ID
, c.StartDateMonth
from (
--Compare record values to rule a record in/out based on OP's logic
select b.ID
, b.StartDateMonth
, case when b.StartDateMonth = b.StartDateMonthPrev then 0 --still the same month?
when b.StartDateMonth = b.StartDateMonthPrev + 1 then 0 --immediately prior month?
when b.StartDateMonth = 1 and b.StartDateMonthPrev = 12 then 0 --Dec/Jan combo
else 1
end as IncludeFlag
from (
--pull StartDateMonth of previous record into current record
select a.ID
, datepart(mm, a.StartDate) as StartDateMonth
, lag(datepart(mm, a.StartDate), 1, NULL) over (partition by a.ID order by a.StartDate asc) as StartDateMonthPrev
from #table1 as a
) as b
) as c
where 1=1
and c.IncludeFlag = 1
Output:
+----+----------------+
| ID | StartDateMonth |
+----+----------------+
| 1 | 1 |
| 2 | 1 |
| 2 | 3 |
| 3 | 2 |
| 3 | 5 |
+----+----------------+
Try the below query,
SELECT ID,MIN(YEARMONTH) AS YEARMONTH
FROM (
SELECT ID
,YEAR([StartDate])*100+MONTH([StartDate]) AS YEARMONTH
,LAG(YEAR([StartDate])*100+MONTH([StartDate]))
OVER(ORDER BY ID) AS PREVYEARMONTH
,ROW_NUMBER() OVER(ORDER BY ID) AS ROW_NO
FROM #Table1
GROUP BY ID,((YEAR([StartDate])*100)+MONTH([StartDate]))
) AS T
GROUP BY ID
,(CASE WHEN YEARMONTH - PREVYEARMONTH > 1 THEN ROW_NO ELSE 0 END)
ORDER BY ID
Output:
ID YEARMONTH
1 201701
2 201701
2 201703
3 201702
3 201705
Thank you all guys. most of the logic seemed to work..but I tried just with below one and I Was good with thiis.
SELECT t1.ID, DatePart(month,t1.Startdate) AS StartDateMonth
FROM DatesTable t1
LEFT JOIN DatesTable t2
ON t1.ID = t2.ID
AND DatePart(month,t1.Startdate) = DatePart(month,t2.Startdate)+1
WHERE t2.ID IS NULL;
Thanks again
Ok, I wrote my first query without checking, believed that will work correctly. This is my updated version, should be faster than other solutions
select
id
, min(st)%12 --this will return start month
, min(st)/12 + 1 --this will return year, just in case if you need it
from (
select
id, st, gr = st - row_number() over (partition by ID order by st)
from (
select
distinct ID, st = (year(StartDate) - 1) * 12 + month(StartDate)
from
#table2
) t
) t
group by id, gr

Please advise my the stored procedure which is group by and total which different criteria

I have answered one of the interview questions as below.
There are two tables (employee and Department).
Show report No. of people(count) and total salary where IT Dept. salary from 250 to 500 and Sales Dept. salary from 250 to 1000 and Marketing Dept. salary from 250 to 1500.
Sample expected result below
Marketing 0 0.00
Information Technology 1 250.00
Sales 2 1200.00
Employee table
EmpID EmpName DeptID Salary
1 Mike 1 1000.00
2 Paul 1 1500.00
3 John 1 2000.00
4 Joe 2 500.00
5 Kim 3 2000.00
6 Lim 3 2500.00
7 Sam 2 700.00
8 Mario 1 250.00
Department table
DeptID DeptCode DeptName
1 IT Information Technology
2 ST Sales
3 MT Marketing
My Answer:
ALTER PROCEDURE [dbo].[TheseAndThat]
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
SELECT dd.DeptName, ISNULL(TT.c,0) AS StaffCount , ISNULL(TT.s,0) AS TotalSalary FROM [dbo].[Department] dd
LEFT JOIN
(
SELECT d.DeptCode AS dcode, COUNT(*) as c, SUM(e.Salary) as s FROM [dbo].[Employee] e
JOIN [dbo].[Department] d
ON e.DeptID = d.DeptID
WHERE e.Salary between 250 and 500 AND d.DeptID = 1
GROUP BY e.DeptID, d.DeptCode
UNION
SELECT d.DeptCode AS dcode, COUNT(*) as c, SUM(e.Salary) as s FROM [dbo].[Employee] e
JOIN [dbo].[Department] d
ON e.DeptID = d.DeptID
WHERE e.Salary between 250 and 1000 AND d.DeptID = 2
GROUP BY e.DeptID, d.DeptCode
UNION
SELECT d.DeptCode AS dcode, COUNT(*) as c, SUM(e.Salary) as s FROM [dbo].[Employee] e
JOIN [dbo].[Department] d
ON e.DeptID = d.DeptID
WHERE e.Salary between 250 and 1500 AND d.DeptID = 3
GROUP BY e.DeptID, d.DeptCode
) TT
ON dd.DeptCode = TT.dcode
ORDER BY TT.c
END
I'm not sure my answer is correct. However, the result seems to be ok.
Please advise.
If I was you I will go this this query (only 1 time scan to employee table)
SELECT d.DeptName, ISNULL(e.NoEmp,0) AS NoEmp, ISNULL(SumSalary,0) AS SumSalary
FROM [dbo].[Department] AS d
LEFT JOIN (
SELECT DeptID, COUNT(EmpID) As NoEmp, SUM (Salary) AS SumSalary
FROM [dbo].[Employee]
WHERE Salary BETWEEN 250 AND CASE WHEN DeptID = 1 THEN 500
WHEN DeptID = 2 THEN 1000
WHEN DeptID = 3 THEN 1500
END
GROUP BY DeptID) AS e ON d.DeptID = e.DeptID
WHERE d.DeptID IN(1,2,3)
First, the code to setup temp tables:
declare #employees table
(
EmpID int,
EmpName varchar(100),
DeptID int,
Salary decimal
)
declare #departament table
(
DeptID int,
DeptCode char(2),
DeptName varchar(100)
)
insert into #employees values (1,'Mike',1,1000.00)
insert into #employees values (2,'Paul',1,1500.00)
insert into #employees values (3,'John',1,2000.00)
insert into #employees values (4,'Joe',2,500.00)
insert into #employees values (5,'Kim',3,2000.00)
insert into #employees values (6,'Lim',3,2500.00)
insert into #employees values (7,'Sam',2,700.00)
insert into #employees values (8,'Mario',1,250.00)
insert into #departament values (1, 'IT', 'Information Technology')
insert into #departament values (2, 'ST', 'Sales')
insert into #departament values (3, 'MT', 'Marketing')
Now, the report:
select DeptName, COALESCE(d2.DeptID, 0), COALESCE(Salaries,0) from #departament d2
left join
(
select COUNT(*) as DeptID, SUM(Salary) as Salaries from #departament d
inner join #employees e on d.DeptID = e.DeptID
where
(d.DeptID = 1 and e.Salary between 250 and 500)
or
(d.DeptID = 2 and e.Salary between 250 and 1000)
or
(d.DeptID = 3 and e.Salary between 250 and 1500)
group by d.DeptID) as sums on sums.DeptID = d2.DeptID
An alternative (uses the same temp tables as #GustavoF answer):
DECLARE #Input TABLE( DeptID INT, SalaryRangeMin decimal, SalaryRangeMax decimal )
INSERT INTO #Input
VALUES
( 1, 250, 500),
( 2 ,250 ,1000 ),
( 3 ,250 , 1500 )
SELECT D.DeptName, COUNT(EmpID) as EmployeeCount, ISNULL( SUM( e.Salary ), 0.0 ) as TotalSalary
FROM #Input AS I
INNER JOIN #departament AS D ON I.DeptID = D.DeptID
LEFT JOIN #Employees AS E ON I.DeptID = E.DeptID AND E.Salary BETWEEN I.SalaryRangeMin AND I.SalaryRangeMax
GROUP BY E.DeptID, D.DeptCode, D.DeptName
ORDER BY TotalSalary ASC
Output:
DeptName EmployeeCount TotalSalary
--------------------------- ------------- -------------
Marketing 0 0
Information Technology 1 250
Sales 2 1200

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

Resources