Updating Using Aggregate Function - sql

I am trying to update a column of the table using the below query.. But I get an error
An aggregate may not appear in the set list of an UPDATE statement
Code:
UPDATE Test.dbo.Table1
SET InDate = MIN(b.Date)
FROM
Test.dbo.Table1 a
LEFT OUTER JOIN
Test.dbo.Table2 b
ON
a.ID1 = b.ID2
WHERE b.Code = 'IN';
I want to update the InDate column in my table with the oldest date from Table2 (b.Date) column where (b.code) is 'IN'
What is wrong in here?

You need to put the aggregate in a temp table or subquery and you need an explicit GROUP BY statement.
UPDATE Test.dbo.Table1
SET InDate = min_date
FROM Test.dbo.Table1 c inner join
(SELECT a.id1, MIN(b.Date) min_date
FROM Test.dbo.Table1 a
LEFT OUTER JOIN Test.dbo.Table2 b
ON a.ID1 = b.ID2
Group by a.id1) d
ON c.ID1 = d.ID1
WHERE c.Code = 'IN';

I think this will do what you want. I've removed the aliases to make it as clear as possible:
UPDATE Table1
SET InDate = (
SELECT MIN(Table2.Date)
FROM Table2
WHERE Table1.ID1 = Table2.ID2
AND Table2.Code = 'IN'
)

You could use apply to get the min date and then use that in the update statement:
UPDATE a
SET a.InDate = b.MinBDate
FROM Table1 a
OUTER APPLY
(
SELECT MIN(b.InDate) MinBDate
FROM Table2 b
WHERE b.Id = a.Id
AND b.Code = 'IN'
) b

Maybe this?
UPDATE Test.dbo.Table1
SET InDate = b.Date
FROM
Test.dbo.Table1 a
INNER JOIN (
select
b.ID2,
MIN(b.Date) Date
from Test.dbo.Table2 b
where
WHERE b.Code = 'IN'
group by
b.ID2
) b
ON
a.ID1 = b.ID2

Assuming your data model is something like the following, joining to a derived table should do the trick:
--Data Setup:
DECLARE #Table1 TABLE (ID1 INT, InDate DATETIME)
DECLARE #Table2 TABLE (ID2 INT, ID1 INT, Date DATETIME, Code VARCHAR(12))
INSERT INTO #Table1 (ID1)
VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10)
INSERT INTO #Table2 (ID2, ID1, Date, Code)
VALUES
(1, 1, '1/1/2014', 'OUT'),
(2, 1, '5/1/2014', 'IN'),
(3, 1, '3/1/2013', 'IN'),
(4, 2, '1/1/2014', 'OUT'),
(5, 2, '1/1/2014', 'IN'),
(6, 3, '1/1/2014', 'IN'),
(7, 4, '1/1/2014', 'IN'),
(8, 5, '1/1/2014', 'IN'),
(9, 6, '2/1/2014', 'OUT'),
(10, 7, '3/1/2014', 'IN'),
(11, 8, '4/1/2014', 'IN'),
(12, 9, '2/1/2014', 'IN'),
(12, 9, '2/1/2014', 'IN'),
(12, 10, '1/2/2014', 'IN'),
(12, 10, '1/3/2014', 'IN'),
(12, 10, '1/4/2014', 'IN'),
(12, 10, '1/1/2014', 'OUT')
--Actual Update:
UPDATE T1
SET InDate = T2.MinDate
FROM #Table1 T1
JOIN (SELECT T2.ID1, MIN(Date) AS MinDate
FROM #Table2 T2
WHERE T2.Code = 'IN'
GROUP BY T2.ID1) T2 ON T2.ID1 = T1.ID1
--Results
SELECT *
FROM #Table1

Related

JOIN 2 tables based on StartDate and EndDate column

I have 2 tables as described in the SQL below:
DECLARE #Table1 TABLE
(
[ForeignKeyID] INT
,[Name] CHAR
,[StartDate] DATE
,[FinishDate] DATE
);
DECLARE #Table2 TABLE
(
[ForeignKeyID] INT
,[StartDate] DATE
,[EndDate] DATE
);
INSERT INTO #Table1 ([ForeignKeyID], [Name], [StartDate], [FinishDate])
VALUES (1, 'A', '20210101', '20210103')
,(1, 'B', '20210103', NULL);
INSERT INTO #Table2 ([ForeignKeyID], [StartDate], [EndDate])
VALUES (1, '20210101', '20210102')
,(1, '20210102', '20210103')
,(1, '20210103', '20210104')
,(1, '20210104', '20210105')
,(1, '20210105', '20210106');
SELECT
t2.ForeignKeyID, t2.StartDate, t2.EndDate, t1.Name AS OnDuty
FROM
#Table2 t2
INNER JOIN #Table1 t1 ON t2.ForeignKeyID = t1.ForeignKeyID
-- WHERE ???
ORDER BY t2.StartDate;
I want to get a result table as shown below. I have tried, but the issue is with the NULL values in the FinishDate column in Table1.
ForeignKeyID
StartDate
EndDate
OnDuty
1
1-Jan-21
2-Jan-21
A
1
2-Jan-21
3-Jan-21
A
1
3-Jan-21
4-Jan-21
B
1
4-Jan-21
5-Jan-21
B
1
5-Jan-21
6-Jan-21
B
I'm guessing at your logic here (be best if you could add the logic to your question), but a combination or and / or logic to compare the dates depending on whether FinishDate is null or not should do the trick.
SELECT t2.ForeignKeyID, t2.StartDate, t2.EndDate, t1.[Name] AS OnDuty
FROM #Table2 t2
INNER JOIN #Table1 t1 ON t2.ForeignKeyID = t1.ForeignKeyID
WHERE t2.StartDate >= t1.StartDate
AND (t2.StartDate < t1.FinishDate OR t1.FinishDate IS NULL)
ORDER BY t2.StartDate;

SQL: Getting previous record from other table

I want to get the previous record of each record in Table A from Table B.
for easy, below is the table sample data:
drop table if exists #A
drop table if exists #B
CREATE TABLE #A(Name varchar(10), time datetime, value int)
insert into #A values
('A', '2020-03-31 18:00:00', 56),
('A', '2020-03-31 19:00:00', 3),
('B', '2020-03-31 14:00:00', 14),
('C', '2020-03-31 15:00:00', 26)
CREATE TABLE #B(Name varchar(10), time datetime, value int)
insert into #A values
('A', '2020-03-31 21:00:00', 79),
('A', '2020-03-31 17:00:00', 44),
('A', '2020-03-31 14:00:00', 76),
('B', '2020-03-31 18:00:00', 89),
('C', '2020-03-31 11:00:00', 29),
('C', '2020-03-31 08:00:00', 6)
EDIT:
It should include only last previous record from TableB.
Sorry for the confusion. Changed image and sample data also.
I think you want:
select a.name, a.time, a.value
from #a a
union all
select b.name, b.time, b.value
from (select b.*, row_number() over (order by time desc) as seqnum
from #b b
where b.time < (select min(a.time)
from #a a
where a.name = b.name
)
) b
where seqnum = 1
order by name, time;
Here is a db<>fiddle.
EDIT:
If b could have multiple "previous" records, then:
select a.name, a.time, a.value
from #a a
union all
select b.name, b.time, b.value
from (select b.*,
row_number() over (partition by b.name order by b.time desc) as seqnum
from #b b
where b.time < (select min(a.time)
from #a a
where a.name = b.name
)
) b
where seqnum = 1
order by name, time;
Here is a db<>fiddle for this version.

SQL statement to get all customers with no orders TODAY(current date)

The question is, how do I write a statement that would return all customers with NO Orders TODAY using sql join?
Tables : tbl_member ,tbl_order
tbl_member consist of id,name,
tbl_order consist of id, date, foodOrdered
If you left join, the select where the table on the right is nulkl, it limits to the rows that DO NOT meet the join condition:
select t1.*
from tbl_member t1
left join tbl_member t2
on t1.id = t2.id -- assuming that t2.id relates to t1.id
and t2.date = current_date() -- today's date in mysql
where t2.id is null
Assuming tbl_order date is a datetime (it probably should be) for sql server you could use something like:
declare #tbl_member table
(
id int,
fullname varchar(50)
)
declare #tbl_order table
(
id int,
orderdate datetime,
foodOrdered varchar(50)
)
INSERT INTO #tbl_member VALUES (1, 'George Washington')
INSERT INTO #tbl_member VALUES (2, 'Abraham Lincoln')
INSERT INTO #tbl_member VALUES (3, 'Mickey Mouse')
INSERT INTO #tbl_member VALUES (3, 'Donald Duck')
INSERT INTO #tbl_order VALUES (1, '2017-07-01 13:00:00', 'Fish and Chips')
INSERT INTO #tbl_order VALUES (2, '2017-07-03 08:00:00', 'Full English')
INSERT INTO #tbl_order VALUES (3, '2017-07-25 08:00:00', 'Veggie Burger')
INSERT INTO #tbl_order VALUES (3, '2017-07-25 12:00:00', 'Bangers and Mash')
SELECT id, fullname FROM #tbl_member WHERE id NOT IN
(SELECT id FROM #tbl_order
WHERE CAST(orderDate as date) = CAST(GETDATE() as Date))
It helps if you specify what flavour database you are using as the syntax is often subtly different.

SQL Table data compare

I need to compare the two tables rows and also only show the coulmns having different data i.e mismatch data from both the table.Suppose Table1 and Table2 having 50 Columns and in that only mistach records are 5 then that coulms needs into Select statement.
Comparsion part is completed with Union query, My hurdle is how to come up with mismacted row columns names.
One way to do this is to have a list of all such column names concatenated in one string as:
select
T1.id, case when t1.col1<> t2.col1 then 'Col1;' else '' end +
case when t1.col2<> t2.col2 then 'Col2;' else '' end
-- similar case statementes for all th columns you want to be included
-- in the list
as Mismatchedcolumns
from Table1 T1
Join Table2 T2 on T1.id = T2.id
Check Demo here..
If you are looking at a list of all mismatched columns, then see below example
CREATE TABLE TableA
([Product] varchar(1), [Qty] int, [Price] int, [Comments] varchar(3))
;
INSERT INTO TableA
([Product], [Qty], [Price], [Comments])
VALUES
('A', 20, 500, 'xyz'),
('B', 50, 200, 'xyz'),
('C', 90, 100, 'abc'),
('D', 50, 500, 'xyz')
;
CREATE TABLE TableB
([Product] varchar(1), [Qty] int, [Price] int, [Comments] varchar(3))
;
INSERT INTO TableB
([Product], [Qty], [Price], [Comments])
VALUES
('B', 70, 200, 'cv'),
('C', 90, 200, 'wsd'),
('D', 40, 400, 'xyz'),
('E', 50, 500, 'xyz')
;
SELECT b.Product,
b.Qty,
b.Price,
Result = CASE WHEN a.product IS NULL THEN 'New'
ELSE 'Updated: ' +
STUFF( CASE WHEN a.Qty != b.Qty THEN ',Qty' ELSE '' END +
CASE WHEN a.Price != b.Price THEN ',Price' ELSE '' END,
1, 1, '')
END
FROM TableB b
LEFT JOIN TableA a
ON a.Product = b.Product
WHERE a.Product IS NULL
OR a.Qty != b.Qty
OR a.Price != b.Price
union
SELECT
a.Product,a.Qty,a.Price, 'NewA' as Result
FROM
TABLEA a left join
TABLEB b on a.Product = b.Product
WHERE b.Product is null
Modified version of solution at SQL Server 2008 compare two tables in same database and get column is changed
http://sqlfiddle.com/#!3/d1b3f/3

Merging records based on a time difference?

I have the following table:
CREATE TABLE #TEMP (id int, name varchar(255), startdate datetime, enddate datetime)
INSERT INTO #TEMP VALUES(1, 'John', '2011-01-11 00:00:00.000','2011-01-11 00:01:10.000')
INSERT INTO #TEMP VALUES(2, 'John', '2011-01-11 00:00:20.000','2011-01-11 00:01:50.000')
INSERT INTO #TEMP VALUES(3, 'John', '2011-01-11 00:01:40.000','2011-01-11 00:01:50.000')
INSERT INTO #TEMP VALUES(4, 'Adam', '2011-01-11 00:00:40.000','2011-01-11 00:01:20.000')
INSERT INTO #TEMP VALUES(5, 'Adam', '2011-01-11 00:00:10.000','2011-01-11 00:01:30.000')
SELECT * FROM #TEMP
DROP TABLE #TEMP
I am trying to merge all records with the same name within a range of 60 seconds to each other to get the following:
John 2011-01-11 00:00:00.000 2011-01-11 00:01:10.000
John 2011-01-11 00:01:40.000 2011-01-11 00:01:50.000
Adam 2011-01-11 00:00:10.000 2011-01-11 00:01:20.000
Any suggestions on how to do this on a table with about 50K records? Currently, I managed to get to this:
SELECT * FROM #TEMP
CREATE TABLE #Merge(id1 int, id2 int)
INSERT INTO #Merge
SELECT id, uuid
FROM
(
SELECT t.id, u.uuid, t.name, t.startdate, t.enddate, u.ustartdate, u.uenddate,
(CASE WHEN (DATEDIFF(second, t.startdate, u.ustartdate) <= 60 AND DATEDIFF(second, t.startdate, u.ustartdate) >= 0) then 1 else 0 END) Flag
FROM #Temp t
INNER JOIN
(SELECT id AS uuid, name, startdate AS ustartdate, enddate AS uenddate
FROM #Temp) u
ON t.name = u.name AND t.startdate != u.ustartdate AND t.id != u.uuid
) w
WHERE Flag = 1
SELECT * FROM #Merge
-- Insert non-mergable records
CREATE TABLE #TEMP2 (id int, name varchar(255), membergroup varchar(255), startdate datetime, enddate datetime)
INSERT INTO #TEMP2
SELECT * FROM #TEMP
WHERE id NOT IN (SELECT id1 FROM #Merge UNION SELECT id2 FROM #Merge)
SELECT * FROM #TEMP2
Of course, I am not sure how to proceed from here. The #Merge table gives me rows that are to be merged. What I did was to insert non-mergable rows first into #Temp2 first.
EDIT:
Updated set of rows, just in case:
INSERT INTO #TEMP VALUES(1, 'John', 'A', '2011-01-11 00:00:00.000','2011-01-11 00:01:10.000')
INSERT INTO #TEMP VALUES(2, 'John', 'A', '2011-01-11 00:00:01.000','2011-01-11 00:01:10.000')
INSERT INTO #TEMP VALUES(3, 'John', 'B', '2011-01-11 00:00:20.000','2011-01-11 00:01:50.000')
INSERT INTO #TEMP VALUES(4, 'John', 'C', '2011-01-11 00:01:40.000','2011-01-11 00:01:50.000')
INSERT INTO #TEMP VALUES(5, 'John', 'C', '2011-01-11 00:01:50.000','2011-01-11 00:02:20.000')
INSERT INTO #TEMP VALUES(6, 'Adam', 'A', '2011-01-11 00:00:40.000','2011-01-11 00:01:20.000')
INSERT INTO #TEMP VALUES(7, 'Adam', 'B', '2011-01-11 00:00:10.000','2011-01-11 00:01:30.000')
INSERT INTO #TEMP VALUES(8, 'Adam', 'B', '2011-01-11 00:03:10.000','2011-01-11 00:04:30.000')
The code below manage's to show both merged rows (rows 1-2,4-5) and unique rows (row 3)
SELECT DISTINCT a.id,a.name,a.startdate,a.enddate
FROM temp a
LEFT JOIN temp b ON a.name = b.name AND a.id < b.id AND DATEDIFF(s,a.startdate,b.startdate)<=60
LEFT JOIN temp c ON c.name = a.name AND c.id < a.id AND DATEDIFF(s,c.startdate,a.startdate)<=60
WHERE (b.id IS NOT NULL OR c.id IS NULL) AND a.id <= COALESCE(c.id,a.id)
Given you haven't said how to use the 60 second interval and your sample code showed only a startdate comparison, here you go
SELECT
*
FROM
#Temp t1
CROSS APPLY
(SELECT TOP 1*
FROM #Temp t2
WHERE t1.name = t2.name AND DATEDIFF(second, t1.startdate, t2.startdate) < 60 AND t1.id < t2.id
ORDER BY id DESC
) t2x
Based on startdate only, row pairs 1/2 and 4/5 make it into the output. Row 3 doesn't so you'll have to explain why you added it.
That is, row id = 3 is not within 60 seconds of row 1 or 2 based on startdate. So it shouldn't be in the output.
This assumes that id and startdate are both increasing.
Edit, after chat:
SELECT
*
FROM
#Temp t1
CROSS APPLY
(SELECT TOP 1 *
FROM #Temp t2
WHERE t1.name = t2.name AND DATEDIFF(second, t1.startdate, t2.startdate) < 60 AND t1.id < t2.id
ORDER BY t2.id DESC
) t2x
UNION ALL
SELECT
t1.*, t1.*
FROM
#Temp t1
WHERE NOT EXISTS
(
SELECT
t1ZZ.id, t2xZZ.id
FROM
#Temp t1ZZ
CROSS APPLY
(SELECT TOP 1 *
FROM #Temp t2ZZ
WHERE t1ZZ.name = t2ZZ.name AND DATEDIFF(second, t1ZZ.startdate, t2ZZ.startdate) < 60 AND t1ZZ.id < t2ZZ.id
ORDER BY t2ZZ.id DESC
) t2xZZ
WHERE
t1.id IN (t1ZZ.id, t2xZZ.id)
)