SQL Server: How to update table based on subquery in where clause? - sql

I have a table (with data) like this:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[lTab](
[log_id] [int] IDENTITY(1,1) NOT NULL,
[JobName] [nvarchar](40) NULL,
[startTime] [datetime] NULL,
[endTime] [datetime] NULL,
[BatchId] [int] NULL,
[status] [varchar](10) NULL,
[messag] [varchar](255) NULL
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
SET IDENTITY_INSERT [dbo].[lTab] ON
INSERT [dbo].[lTab] ([log_id], [JobName], [startTime], [endTime], [BatchId], [status], [messag]) VALUES (1, N'Job1', CAST(0x00009EB700FBF56F AS DateTime), NULL, 2, N'START', N'Test')
INSERT [dbo].[lTab] ([log_id], [JobName], [startTime], [endTime], [BatchId], [status], [messag]) VALUES (2, N'Job2', NULL, CAST(0x00009EB700FBF975 AS DateTime), 2, N'START', N'Test')
INSERT [dbo].[lTab] ([log_id], [JobName], [startTime], [endTime], [BatchId], [status], [messag]) VALUES (3, N'Job3', CAST(0x00009EB700FC287F AS DateTime), NULL, 2, N'START', N'Test')
INSERT [dbo].[lTab] ([log_id], [JobName], [startTime], [endTime], [BatchId], [status], [messag]) VALUES (4, N'Job3', NULL, CAST(0x00009EB700FC2CC6 AS DateTime), 2, N'END', N'Test')
SET IDENTITY_INSERT [dbo].[lTab] OFF
I'm trying to update endTime based on Jobname and max(log_id).
DECLARE #Jname VARCHAR(10)
SET #Jname = 'Job3'
UPDATE lTab
SET endTime = GETDATE()
WHERE log_id = (SELECT JobName, MAX(log_id) AS log_id FROM dbo.lTab WHERE jobname = #Jname GROUP BY JobName)
I get an error
sg 116, Level 16, State 1, Line 6
Only one expression can be specified in the select list when the subquery is not introduced with EXISTS.
How to get this work?

WITH q AS
(
SELECT *,
ROW_NUMBER() OVER (PARTITION BY JobName ORDER BY log_id DESC) AS rn
FROM lTab
WHERE JobName = #Jname
)
UPDATE q
SET endTime = GETDATE()
WHERE rn = 1

Take out the Jobname from the select list in the subquery.
You don't actually need it to get the result you need, SQL Server will still return the right log_id.
What you have won't work since you are returning 2 fields (Jobname,MAX(Log_id)) and trying to match Log_id to it.

This query:
(SELECT JobName, MAX(log_id) AS log_id FROM dbo.lTab WHERE jobname = #Jname GROUP BY JobName)
is returning multiple results, exclude JobName from your query.

Yet a better option would be
UPDATE tablename SET tablename.field1 = sourceTable.dataField
From tablename
join sourceTable On tablename.keyfield = sourceTable.keyField
Where sourceTable.jobname = #jobName

Related

Complex Pivot table in SQL Server

I have some data that looks like:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[tblMyLog]
(
[Pkid] [bigint] IDENTITY(1,1) NOT NULL,
[JobId] [int] NOT NULL,
[log_recorded] [datetime] NOT NULL,
[status] [nvarchar](max) NULL
)
GO
SET IDENTITY_INSERT [dbo].[tblMyLog] ON
GO
INSERT [dbo].[tblMyLog] ([Pkid], [JobId], [log_recorded], [status])
VALUES (1, 1, CAST(N'2021-06-29T15:35:09.917' AS DateTime), N'Started')
GO
INSERT [dbo].[tblMyLog] ([Pkid], [JobId], [log_recorded], [status])
VALUES (2, 1, CAST(N'2021-06-29T15:36:08.810' AS DateTime), N'Ended')
GO
INSERT [dbo].[tblMyLog] ([Pkid], [JobId], [log_recorded], [status])
VALUES (3, 2, CAST(N'2021-06-29T15:33:41.133' AS DateTime), N'Started')
GO
INSERT [dbo].[tblMyLog] ([Pkid], [JobId], [log_recorded], [status])
VALUES (4, 2, CAST(N'2021-06-29T15:35:09.917' AS DateTime), N'Ended')
GO
I would like to get in a format like so I do some joins to tblMyLog from another table:
JobId StartTime EndTime
------------------------------------------------------
1 2021-06-29 15:35:09.917 2021-06-29 15:36:08.810
2 2021-06-29 15:33:41.133 2021-06-29 15:35:09.917
but I can't figure out the pivot table syntax to do this.
Assuming a JobID can have multiple start/end times, we can use the window function sum() over() to create an ad-hoc group Grp
Example
Select JobID
,StartTime = min( case when status='Started' then log_recorded end)
,EndTime = max( case when status='Ended' then log_recorded end)
From (
Select *
,Grp = sum( case when status='Started' then 1 end) over ( partition by JobID order by log_recorded)
From [tblMyLog]
) A
Group By JobID,Grp
Results
JobID StartTime EndTime
1 2021-06-29 15:35:09.917 2021-06-29 15:36:08.810
2 2021-06-29 15:33:41.133 2021-06-29 15:35:09.917

MS SQL Server 2008 query - doesn't get proper send and received message count

SELECT DISTINCT
U.UserId as 'Id',
U.FullName as 'Name',
(SELECT COUNT(*) FROM [Conversation]
WHERE FromUserId = 'user1' AND ToUserId = U.UserId) 'SentCount',
(SELECT COUNT(*) FROM [Conversation]
WHERE ToUserId = 'user1' AND FromUserId = U.UserId) 'ReceivedCount'
FROM
[Conversation] C
INNER JOIN
[User] U ON U.UserId = C.FromUserId
WHERE
C.ToUserId = 'user1'
Query returns a result but it doesn't include some of the rows. Conversation table contains the same FromUserId (send message user) and ToUserId (receive message user).
Here are the tables :
Current result -
Expected result:
Table with dummy data -
CREATE TABLE [dbo].[User](
[Id] [int] NULL,
[UserId] [varchar](5) NULL,
[Name] [varchar](5) NULL,
[Email] [varchar](11) NULL
) ON [PRIMARY]
INSERT [dbo].[User] ([Id], [UserId], [Name], [Email]) VALUES (1, N'user1', N'user1', N'user1#a.com')
INSERT [dbo].[User] ([Id], [UserId], [Name], [Email]) VALUES (2, N'user2', N'user2', N'user2#a.com')
INSERT [dbo].[User] ([Id], [UserId], [Name], [Email]) VALUES (3, N'user3', N'user3', N'user3#a.com')
INSERT [dbo].[User] ([Id], [UserId], [Name], [Email]) VALUES (4, N'user4', N'user4', N'user4#a.com')
INSERT [dbo].[User] ([Id], [UserId], [Name], [Email]) VALUES (5, N'user5', N'user5', N'user5#a.com')
CREATE TABLE [dbo].[Conversation](
[Id] [int] NULL,
[conversationId] [varchar](14) NULL,
[messageId] [varchar](4) NULL,
[fromUserId] [varchar](5) NULL,
[toUserId] [varchar](5) NULL
) ON [PRIMARY]
INSERT [dbo].[Conversation] ([Id], [conversationId], [messageId], [fromUserId], [toUserId]) VALUES (1, N'con-user1user2', N'mes1', N'user1', N'user2')
INSERT [dbo].[Conversation] ([Id], [conversationId], [messageId], [fromUserId], [toUserId]) VALUES (2, N'con-user1user2', N'mes2', N'user1', N'user2')
INSERT [dbo].[Conversation] ([Id], [conversationId], [messageId], [fromUserId], [toUserId]) VALUES (3, N'con-user2user1', N'mes3', N'user2', N'user1')
INSERT [dbo].[Conversation] ([Id], [conversationId], [messageId], [fromUserId], [toUserId]) VALUES (4, N'con-user1user3', N'mes4', N'user1', N'user3')
INSERT [dbo].[Conversation] ([Id], [conversationId], [messageId], [fromUserId], [toUserId]) VALUES (5, N'con-user4user1', N'mes5', N'user4', N'user1')
Can someone help how to includes all the records?
Thanks!
You don't need a join in the outer query. This would be more simply written as:
SELECT U.UserId as Id, U.FullName as Name,
(SELECT COUNT(*)
FROM [Conversation] c
WHERE c.FromUserId = 'user1' AND c.ToUserId = U.UserId
) as SentCount,
(SELECT COUNT(*)
FROM [Conversation] c
WHERE c.ToUserId = 'user1' AND c.FromUserId = U.UserId
) as ReceivedCount
FROM [User] U ;
Notes:
Qualify all column references. This is particularly important for correlation clauses.
Give tables aliases that are abbreviations for the table names.
Only use single quotes for string and date constants. Do not use the for column aliases.
Here is a db<>fiddle.
You don't need the conversation table at your base select. Something like this would work, but can be optimised with subqueries:
Select U.UserId as 'Id',
U.name as 'Name',
isnull(fromSummed.sentCount, 0) 'SentCount',
isnull(ToSummed.ReceivedCount, 0) 'ReceivedCount'
FROM [User] U
outer apply (select count(*) as sentCount from [Conversation] cFrom where cFrom.FromUserId = 'user1' and ToUserId = U.UserId ) fromSummed
outer apply (select count(*) as ReceivedCount from [Conversation] cTo where cTo.ToUserId = 'user1' and cTo.FromUserId = U.UserId) ToSummed
where isnull(fromSummed.sentCount, 0)>0 or isnull(ToSummed.ReceivedCount, 0)>0

SELECT data from multiple tables using max?

I have Project and Score tables like this
How do I query to get this result, to show all ProjectID, ProjectName, and its newest Score (latest Date) with the Date:
I tried:
SELECT R.ProjectID, Name, Score, Date
FROM PWINProject, PWINRecord R
WHERE Date = (
SELECT max(Date)
FROM PWINRecord
WHERE ProjectID = R.ProjectID
)
AND PWINProject.ProjectID = R.ProjectID
But it only shows me projects with a score, when a project doesn't have a score yet (e.g. #3 - Amazon) it won't show.
You need a left join instead on an inner join (and use a proper join not the non-standard old style join you are using). However it also simplifies the query to use a window function to determine what row to return e.g.
declare #PWINProject table (ProjectId int, [Name] varchar(256))
insert into #PWINProject(ProjectId, [Name])
select 1, 'Database'
union all select 2, 'Microsoft'
union all select 3, 'Amazon'
union all select 4, 'IBM'
declare #PWINRecord table (ScoreId int, ProjectId int, Score int, [Date] datetime)
insert into #PWINRecord (ScoreId, ProjectId, Score, [Date])
select 1, 1, 100, '2019-01-15 19:40:46.723'
union all select 2, 1, 52, '2019-01-15 20:40:46.723'
union all select 3, 2, 60, '2019-01-15 21:40:46.723'
union all select 4, 2, 55, '2019-01-15 22:40:46.723'
union all select 5, 2, 72, '2019-01-15 23:41:46.723'
union all select 6, 4, 111, '2019-01-16 10:40:46.723'
union all select 7, 4, 90, '2019-01-17 12:40:46.723'
select ProjectId, [Name], Score, [Date]
from (
SELECT P.ProjectID, [Name], Score, [Date]
, row_number() over (partition by R.ProjectID order by [Date] desc) Row#
FROM #PWINProject P
left join #PWINRecord R on R.ProjectID = P.ProjectID
) X
where X.Row# = 1
order by ProjectId
Returns:
ProjectID Name Score Date
1 Database 52 2019-01-15 20:40:46.723
2 Microsoft 72 2019-01-15 23:41:46.723
3 Amazon NULL NULL
4 IBM 90 2019-01-17 12:40:46.723
PS: This is the recommended style for posting an SQL question, where you setup the data into temp tables or table variables yourself - saves people answering a lot of time. Data in images is a no-no.
One approach would be to use OUTER APPLY:
DECLARE #Project TABLE (ProjectId INT, ProjectName VARCHAR(100))
INSERT INTO #Project
VALUES (1, 'Database'), (2, 'Microsoft'), (3, 'Amazon'), (4, 'IBM')
DECLARE #Score TABLE(ScoreId INT, ProjectId INT, Score INT, RefDate DATE)
INSERT INTO #Score
VALUES (1, 1, 100, '2019-01-01'), (2, 2, 200, '2019-02-02'), (4, 4, 400, '2019-04-04')
SELECT
P.ProjectId,
P.ProjectName,
S.Score,
S.RefDate
FROM #Project P
OUTER APPLY (
SELECT TOP 1 S.*
FROM #Score S
WHERE S.ProjectId = P.ProjectId ORDER BY S.RefDate DESC
) S
You need to be careful outer apply is not very efficient but it's clean and easy to understand. Other techniques may work involving ROW_NUMBER, you should study a bit the execution plan to see what fits best.
Try this:
select P.ProjectID, P.ProjectName, R.Score, R.Date
from PWINProject P
left outer join PWINRecord R on R.ProjectID = P.ProjectID
and R.Date = (
select max(R2.Date)
from PWINRecord R2
where R2.ProjectID = R.ProjectID
)
A simple solution is the following:
select p.ProjectID, p.ProjectName, s.Score as LatestScore, s.[date]
from Project as p
left outer join Scores as s ON p.ProjectID = s.ProjectID
where (
s.[date] = (
select top (1) s2.[date]
from Scores as s2
where s2.ProjectID = s.ProjectID
order by [date] desc
)
or s.[date] is null
)
CREATE TABLE [dbo].[PWINProject](
[ProjectID] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](50) NULL,
CONSTRAINT [PK_PWINProject] PRIMARY KEY CLUSTERED
(
[ProjectID] ASC
) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
CREATE TABLE [dbo].[PWINRecord](
[ScoreId] [int] NOT NULL,
[ProjectID] [int] NULL,
[Score] [int] NULL,
[Date] [datetime] NULL,
CONSTRAINT [PK_PWINRecord] PRIMARY KEY CLUSTERED
(
[ScoreId] ASC
) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
SET IDENTITY_INSERT [dbo].[PWINProject] ON
INSERT [dbo].[PWINProject] ([ProjectID], [Name]) VALUES (1, N'Database')
INSERT [dbo].[PWINProject] ([ProjectID], [Name]) VALUES (2, N'Microsoft')
INSERT [dbo].[PWINProject] ([ProjectID], [Name]) VALUES (3, N'Amazone')
INSERT [dbo].[PWINProject] ([ProjectID], [Name]) VALUES (4, N'IBM')
SET IDENTITY_INSERT [dbo].[PWINProject] OFF
INSERT [dbo].[PWINRecord] ([ScoreId], [ProjectID], [Score], [Date]) VALUES (1, 1, 100, CAST(N'2019-01-15 00:00:00.000' AS DateTime))
INSERT [dbo].[PWINRecord] ([ScoreId], [ProjectID], [Score], [Date]) VALUES (2, 1, 52, CAST(N'2019-01-15 00:00:00.000' AS DateTime))
INSERT [dbo].[PWINRecord] ([ScoreId], [ProjectID], [Score], [Date]) VALUES (3, 2, 60, CAST(N'2019-01-15 00:00:00.000' AS DateTime))
INSERT [dbo].[PWINRecord] ([ScoreId], [ProjectID], [Score], [Date]) VALUES (4, 2, 55, CAST(N'2019-01-15 00:00:00.000' AS DateTime))
INSERT [dbo].[PWINRecord] ([ScoreId], [ProjectID], [Score], [Date]) VALUES (5, 2, 72, CAST(N'2019-01-15 00:00:00.000' AS DateTime))
INSERT [dbo].[PWINRecord] ([ScoreId], [ProjectID], [Score], [Date]) VALUES (6, 4, 111, CAST(N'2019-01-16 00:00:00.000' AS DateTime))
INSERT [dbo].[PWINRecord] ([ScoreId], [ProjectID], [Score], [Date]) VALUES (7, 4, 90, CAST(N'2019-01-17 00:00:00.000' AS DateTime))
ALTER TABLE [dbo].[PWINRecord] WITH CHECK ADD CONSTRAINT [FK_PWINRecord_PWINProject] FOREIGN KEY([ProjectID])
REFERENCES [dbo].[PWINProject] ([ProjectID])
ALTER TABLE [dbo].[PWINRecord] CHECK CONSTRAINT [FK_PWINRecord_PWINProject]
-- To select your desired output run the below code
;WITH CTE
AS
(
SELECT p.ProjectID, p.Name, r.Score, r.[Date], ROW_NUMBER()OVER (PARTITION BY R.ProjectID ORDER BY r.[Date] DESC) RN
FROM PWINProject p
full join PWINRecord R on p.ProjectID =r.ProjectID
)
SELECT ProjectID, Name, Score, [Date]
FROM CTE
WHERE RN = 1

Self join to get the first classID for a report

I have the following table :
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[myTable]
(
[importID] [INT] IDENTITY(1,1) NOT NULL,
[classID] [INT] NOT NULL,
[priorReportID] [INT] NOT NULL,
[currentReportID] [INT] NOT NULL,
[dateStamp] AS (GETDATE())
) ON [PRIMARY]
GO
SET IDENTITY_INSERT [dbo].[myTable] ON
GO
INSERT INTO [dbo].[myTable] ([importID], [classID], [priorReportID], [currentReportID])
VALUES (1, 2069, 3825, 3833), (2, 2069, 3826, 3834),
(3, 2069, 3827, 3835), (4, 2069, 3832, 3836),
(5, 2091, 3889, 3890), (6, 2095, 3894, 3895),
(7, 2098, 3895, 3898), (8, 2098, 3896, 3899),
(9, 2098, 3897, 3900), (10, 2097, 2190, 2193),
(11, 2096, 2188, 2190), (12, 2094, 2187, 2188),
(13, 2093, 2180, 2187)
GO
SET IDENTITY_INSERT [dbo].[myTable] OFF
GO
I am trying to get the first ClassID when a particular report was created.
select *
from mytable
select *
from mytable
where currentReportID = 3833
select *
from mytable
where currentReportID = 3825
select *
from mytable
where currentReportID = 2193
select *
from mytable
where currentReportID = 2190
select *
from mytable
where currentReportID = 2188
select *
from mytable
where currentReportID = 2187
select *
from mytable
where currentReportID = 2180
In the example above: reportID = 2193 was actually created in classID = 2093.
Similarly, reportID = 3833 was created in classID = 2069.
Basically, I need to traverse down the records until there is no more currentReportID = priorReportID condition is satisfied.
Thanks in advance.

how to get table from first table when data is not there in second table

i have requirement where i need to show data of both tables when both the ID's are same.when id is present in first table and not there in second table i need to show data from first table
CREATE TABLE [dbo].[TEST](
[ID] [int] NULL,
[Name] [varchar](10) NULL,
[Status] [char](1) NULL,
[CreatedDate] [datetime] NULL
) ON [PRIMARY]
GO
CREATE TABLE [dbo].[Test_History](
[ID] [int] NULL,
[Name] [varchar](10) NULL,
[Status] [char](1) NULL,
[CreatedDate] [datetime] NULL
) ON [PRIMARY]
GO
/****** Object: Table [dbo].[Test_History] Script Date: 06/19/2015 19:01:49 ******/
INSERT [dbo].[Test_History] ([ID], [Name], [Status], [CreatedDate]) VALUES (1, N'Mohan', N'A', CAST(0x0000A4BC01347E88 AS DateTime))
INSERT [dbo].[Test_History] ([ID], [Name], [Status], [CreatedDate]) VALUES (1, N'Mohan', N'I', CAST(0x0000A4BC0134A390 AS DateTime))
INSERT [dbo].[Test_History] ([ID], [Name], [Status], [CreatedDate]) VALUES (2, N'Rohan', N'A', CAST(0x0000A4BC01391FCC AS DateTime))
/****** Object: Table [dbo].[TEST] Script Date: 06/19/2015 19:01:49 ******/
INSERT [dbo].[TEST] ([ID], [Name], [Status], [CreatedDate]) VALUES (2, N'Rohan', N'I', CAST(0x0000A4BC0138D584 AS DateTime))
INSERT [dbo].[TEST] ([ID], [Name], [Status], [CreatedDate]) VALUES (1, N'Mohan', N'A', CAST(0x0000A4BC013072DC AS DateTime))
INSERT [dbo].[TEST] ([ID], [Name], [Status], [CreatedDate]) VALUES (3, N'Raj', N'A', CAST(0x0000A4BC0138DED7 AS DateTime))
INSERT [dbo].[TEST] ([ID], [Name], [Status], [CreatedDate]) VALUES (4, N'Krishna', N'A', CAST(0x0000A4BC0138EE31 AS DateTime))
so far i have tried my query to achieve the result
select T.ID,COALESCE(T.ID,TT.ID),T.Name,COALESCE(T.Name,TT.Name),T.status,COALESCE(T.status,TT.status)
from Test T LEFT JOIN (Select TOP 1 ID,MIN(Name)name,Status from Test_History
GROUP BY ID,status
)TT
ON T.ID = TT.ID
where T.ID = 3
Id = 1 and 2 present show i will get data from both tables
Id = 3 and 4 not present in the table
so using coalesce i will get the data
from first table and show in 2nd table column also
but is there any other way like both tables are same structure
i'm thinking of
Declare #tablename varchar(10)
IF EXISTS (SELECT 1 from TESt where id = #id)
IF COunt there in both tables
SET #tablename = Test
ELSE
SET #tablename = Test_history
select * from #tablename where id = #ID
can i get any solution like this
You can use EXCEPT.
Here is an example:
SELECT a,b
FROM (
VALUES (1, 2), (3, 4), (5, 6), (7, 8), (9, 10)
) AS MyTable(a, b)
EXCEPT
SELECT a,b
FROM (
VALUES (1, 2), (7, 8), (9, 10)
) AS MyTable(a, b);
This will return all rows of the upper statement, which are not in the second statement.
First: Thanks for the excellent setup for the data related to the question!
If your real question was if table variables can be used as described in your question, the answer is no; or more accurately that its not worth it.
Not recommended:
declare #TableName TABLE (
[ID] [int] NULL,
[Name] [varchar](10) NULL,
[Status] [char](1) NULL,
[CreatedDate] [datetime] NULL)
IF EXISTS (SELECT 1 from TESt where id = #id)
INSERT INTO #TableName SELECT * FROM dbo.TEST WHERE ID = #ID
ELSE INSERT INTO #TableName SELECT * FROM dbo.[Test_History] WHERE ID = #ID
select * from #tablename where id = #ID
Here's the solution I prefer:
DECLARE #ID INT = 3;
SELECT * FROM [dbo].[TEST] ss WHERE ss.id = #id
UNION ALL SELECT * FROM [dbo].[Test_History] th WHERE th.id = #id
and not exists ( SELECT * FROM [dbo].[TEST] ss WHERE ss.id = #id);
UNION ALL performs surprisingly well - don't forget the ALL keyword, and I am assuming that ID is a PK or AK.
If I'm understanding correctly and you want to display all records that match between the two tables and only records from first table when the id does not exist in the second in the same result set, then all you need is a simple left join:
SELECT *
FROM dbo.test t
LEFT OUTER JOIN Test_History th
ON t.id = th.id
WHERE t.id = #id