I have a view that gets the status for each category for each venue.
I need to display all of the above, but only the latest in the status for each category for each venue.
A general idea could be, for each item in a shop, there is a status. So for the cucumbers, letters, and tomatoes in shop A, the status would be good good bad respectively. Same for shop B, C etc. Now these statuses can be updated at any time - but only the latest update should be displayed.
I have tried to create a view such as the following:
SELECT
dbo.vwVenues.HospitalID
, dbo.vwVenues.Hospital AS hospitalName
, dbo.tblVenueStatus.id AS statusId
, dbo.tblVenueStatusName.statusName
, dbo.tblVenueStatusCategories.categoryName
, dbo.tblVenueStatus.dateAdded AS statusDateAdded
, dbo.tblVenueStatus.loggedBy AS statusLoggedBy
, dbo.tblVenueStatusNotes.noteContent
, dbo.tblVenueStatusNotes.dateAdded AS noteDateAdded
, dbo.tblVenueStatusNotes.removed AS noteRemoved
, dbo.tblVenueStatus.resolved AS statusResolved
, dbo.tblVenueStatus.resolveDate AS statusResolveDate
, dbo.tblVenueStatus.removed AS statusRemoved
, dbo.tblVenueStatus.dateRemoved AS statusRemovedDate
, dbo.tblVenueStatus.categoryId
FROM
dbo.tblVenueStatusName
RIGHT OUTER JOIN dbo.tblVenueStatusNotes
RIGHT OUTER JOIN dbo.tblVenueStatus
ON dbo.tblVenueStatusNotes.id = dbo.tblVenueStatus.noteId
LEFT OUTER JOIN dbo.tblVenueStatusCategories
ON dbo.tblVenueStatus.categoryId = dbo.tblVenueStatusCategories.id
ON dbo.tblVenueStatusName.id = dbo.tblVenueStatus.statusNameId
FULL OUTER JOIN dbo.vwVenues
ON dbo.tblVenueStatus.venuId = dbo.vwVenues.HospitalID
I get all the venues, but I get all the statuses for each category, instead of just the latest. Even trying to phrase the question right was hard - so my current search yeilded nothing.
Could anyone please help?
Edit
CREATE TABLE [dbo].[tblVenueStatus](
[id] [int] IDENTITY(1,1) NOT NULL,
[statusNameId] [int] NOT NULL,
[venuId] [int] NOT NULL,
[categoryId] [int] NOT NULL,
[loggedBy] [nvarchar](50) NOT NULL,
[noteId] [int] NOT NULL,
[dateAdded] [datetime] NOT NULL,
[resolved] [tinyint] NOT NULL,
[resolveDate] [datetime] NULL,
[removed] [tinyint] NOT NULL,
[dateRemoved] [datetime] NULL,
CONSTRAINT [PK_tblSiteVenuStatus] PRIMARY KEY CLUSTERED
CREATE TABLE [dbo].[tblVenueStatusCategories](
[id] [int] IDENTITY(1,1) NOT NULL,
[categoryName] [nvarchar](50) NOT NULL,
[categoryDescription] [nvarchar](150) NULL,
CONSTRAINT [PK_tblVenueStatusCategories] PRIMARY KEY CLUSTERED
CREATE TABLE [dbo].[tblVenueStatusName](
[id] [int] IDENTITY(1,1) NOT NULL,
[statusName] [nvarchar](50) NOT NULL,
[statusDescription] [nchar](150) NULL,
CONSTRAINT [PK_tblVenuStatusName] PRIMARY KEY CLUSTERED
CREATE TABLE [dbo].[tblVenueStatusNotes](
[id] [int] IDENTITY(1,1) NOT NULL,
[noteContent] [nvarchar](500) NULL,
[dateAdded] [datetime] NOT NULL,
[removed] [tinyint] NOT NULL,
[dateRemoved] [datetime] NULL,
CONSTRAINT [PK_tblVenueStatusNotes] PRIMARY KEY CLUSTERED
Desired Output will be:
Venu Name | Category 1 Status | Category 2 Status
Try something like this. It's largely sumerised because of what you said you needed to see in your sample. Try this and let me know if it helps. You can add other columns you need here, It is taking the latest status in each category for each hospital, and showing all hospitals and categories in a easily readable grid.
SELECT * FROM
(
SELECT VwV.HospitalID, vwV.Hospital, ISNULL(vStatusName.statusName, '') AS statusName, vStatusCategories.categoryName
FROM
(
SELECT topStatus.HospitalID, topStatus.categoryId, sta2.id
FROM
(
SELECT HospitalID, categoryId, MAX(sta.dateAdded) AS DateAdded
FROM vwVenues
INNER JOIN tblVenueStatus sta ON sta.venuId = dbo.vwVenues.HospitalID
GROUP BY HospitalID, categoryId
) topStatus
INNER JOIN tblVenueStatus sta2 ON sta2.dateAdded = topStatus.DateAdded AND sta2.venuId = topStatus.HospitalID) Statuss
FULL OUTER JOIN vwVenues vwV ON vwV.HospitalID = Statuss.HospitalID
LEFT OUTER JOIN tblVenueStatus vStatus ON vStatus.id = Statuss.id
LEFT OUTER JOIN tblVenueStatusName vStatusName ON vStatusName.id = vStatus.statusNameId
LEFT OUTER JOIN tblVenueStatusCategories vStatusCategories ON vStatus.categoryId = vStatusCategories.id AND vStatusName.id = vStatus.statusNameId
LEFT OUTER JOIN tblVenueStatusNotes vStatusNotes ON vStatusNotes.id = vStatus.noteId
) AS s
PIVOT (MIN(StatusName)
FOR [CategoryName] IN ([Category1],[Category2])) AS PTbl
Related
I have two tables:
db.EmployeeReason
db.employee
with following fields:
CREATE TABLE [dbo].[Employee_Reason](
[Id] [int] IDENTITY(1,1) NOT NULL,
[IdEmployee] [int] NOT NULL,
[IdReason] [int] NOT NULL,
[AverageServiceTime] [time](0) NULL,
[Skillset] [bit] NOT NULL,
CONSTRAINT [PK_Employee_Reason] PRIMARY KEY CLUSTERED ([Id] ASC) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[Employee_Reason] WITH CHECK ADD CONSTRAINT [FK_Employee_Reason_Employee] FOREIGN KEY([IdEmployee])
REFERENCES [dbo].[Employee] ([Id])
GO
where as the employee table looks like this:
CREATE TABLE [dbo].[Employee](
[Id] [int] IDENTITY(1,1) NOT NULL,
[EmployeeId] [int] NOT NULL,
[FirstName] [nvarchar](20) NOT NULL,
[LastName] [nvarchar](40) NOT NULL,
[IdLocation] [int] NOT NULL,
[IdStatus] [int] NOT NULL,
[UserId] [nvarchar](450) NOT NULL,
[IsArchived] [bit] NULL,
[IdAssignedLocation] [int] NOT NULL,
[IdQueue] [int] NOT NULL,
[IdPosition] [int] NOT NULL,
CONSTRAINT [PK_Employee] PRIMARY KEY CLUSTERED ([Id] ASC) ON [PRIMARY]
) ON [PRIMARY]
I want to group employee_reason by idreason and then join with employee using Employee_Reason.idemployee and employee.id like this:
List<SelfServeReason> skillsetReasons =
from reason in db.EmployeeReason
group reason by IdReason into reasonGroup
join employee in scheduleEmployees on reasonGroup.IdEmployee equals employee.Id
select new skillsetReason
{
id = reasonGroup.Id,
description = reasonGroup.Description,
estimatedWaitTime = min(employee.NextAvailableTime) – ConvertToCST(DateTime.Now)
};
I have written it like this:
select MAX(er.Id)
from Employee_Reason er join Employee e on er.IdEmployee = e.id
group by er.IdReason
but not sure how to group employee_reasons and then join this group with idempoyee.
The linq is not correct, it is just pseudocode, if someone can suggest wha will be correct linq that is helpful as well.
Can you please suggest the solution?
If what you want is a SQL query simmilar to that linq query, then you could do:
select er.IdReason, MIN(e.NextAvailableTime) - GETDATE()
from Employee_Reason er
inner join Employee e on er.IdEmployee = e.id
group by er.IdReason
Also, the correct linq would look like this:
var now = ConvertToCST(DateTime.Now);
List<SelfServeReason> skillsetReasons = (
from employee in scheduleEmployees
join reason in db.EmployeeReason on employee.Id equals reason.IdEmployee
group employee by reason.IdReason into reasonGroup
select new SelfServeReason
{
Id = reasonGroup.Key,
EstimatedWaitTime = reasonGroup.Min(p => p.NextAvailableTime).Subtract(now),
}).ToList();
Just bear in mind that I excluded the selection of Description from the queries, as you provided no such column on any table nor is there a table like Reason with that column on it.
Also, the tables you provided have no such columns as Employee.NextAvailableTime, so I'm assuming you forgot to put them there, seeing that it on your linq query.
And least, you provided no spec for that SelfServeReason class, so I'll just go on with filling it with the columns I know your model have.
If that's not the case, please expand your question with this adjustments and I'll edit my answer.
I'm trying to find all customers who have placed an order but there isn't a callback after their last order date.
My current query:
SELECT dbo.[Order].CustomerId, MAX(OrderDate) AS OrderDate, NextCallbackDate, UserName
FROM dbo.[Order]
LEFT OUTER JOIN (SELECT MAX(CallbackDate) NextCallbackDate, CustomerID
FROM AccountCallbacks
GROUP By CustomerId
) callbacks ON callbacks.CustomerID = dbo.[Order].CustomerID
LEFT OUTER JOIN dbo.aspnet_Users users ON dbo.[Order].UserID = users.UserId
WHERE (PaymentTypeID IN (2, 3, 4, 6, 8))
AND OrderDate >= NextCallbackDate
GROUP BY dbo.[Order].CustomerID, dbo.[Order].OrderDate,callbacks.NextCallbackDate, UserName
ORDER BY dbo.[Order].CustomerID
Tables:
AccountCallBacks:
[CallbackID] [int] IDENTITY(1,1) NOT NULL,
[UserID] [uniqueidentifier] NOT NULL,
[CustomerID] [int] NOT NULL,
[Created] [datetime] NOT NULL,
[CallbackDate] [date] NOT NULL,
[Enabled] [bit] NOT NULL,
[CallbackTimeID] [int] NULL,
[GaryCust] [bit] NULL,
[NotInterestedReasonID] [int] NULL
Order Table:
[OrderID] [int] IDENTITY(1,1) NOT NULL,
[CustomerID] [int] NULL,
[UserID] [uniqueidentifier] NULL,
[OrderDate] [datetime] NOT NULL,
[PaymentTypeID] [int] NULL,
[PaymentStatusID] [int] NOT NULL,
[PaymentDate] [datetime] NULL,
[TransactionRef] [varchar](50) NULL
And the aspnet_Users table is the usual .net membership users table
EDIT:
Apologies! I never actual said what was wrong! My query doesn't give me what I'm expecting one of the rows of data for a particular CustomerID isn't in the result set. And no, there should always be at least one AccountCallbacks.CallbackDate for every CustomerID as I'm joining on the dbo.[Order] table and they wouldn't be in there without first ever being in the AccountCallbacks table.
Feel free to ask any other info, help is greatly appreciated.
Query 1 runs fast and uses Parallel execution plan
SELECT c.[Date]
FROM Table1 c
left join Table2 bec on bec.Col1ID = c.Col1ID and bec.Active = 1
WHERE c.Active = 1
AND (c.Col2ID not in (3,4,5,6,7,8,9,10) or c.Col2ID is null)
and (c.[Date] >= '06/12/2014 02:30:00.000 PM')
Query 2 takes longer and uses Normal(Serial) execution plan
SELECT c.[Date]
FROM Table1 c
left join Table2 bec on bec.Col1ID = c.Col1ID and bec.Active = 1
WHERE c.Active = 1
AND (c.Col2ID not in (3,4,5,6,7,8,9,10) or c.Col2ID is null)
and (c.[Date] >= '06/15/2014 02:30:00.000 PM')
Question:
Query2 is trying to get a subset data of Query1, so Query2 should be faster.
Both the Queries differ only by the parameter value, so why completely different execution plans.
Info about the Server: This is running in SQL Server 2008
Table Structure is as follows:
TABLE Table1(
Col1Id [int] IDENTITY(1,1) NOT NULL,
Col2Id [int] NULL,
Col3 [int] NOT NULL,
Col4 [int] NULL,
Active [bit] NOT NULL
[Date] [datetime] NOT NULL)
Index on Table1
Non Clustered on (Active, Date)
TABLE Table2(
[Col] [int] NOT NULL,
Col1ID [int] NOT NULL,
[Col2] [int] NOT NULL,
[Col3] [datetime] NOT NULL,
[Col4] [int] NOT NULL,
[Col5] [datetime] NULL,
[Col6] [int] NULL,
[Active] [bit] NULL)
Index on Table2
Non Clustered on [Active] Included (Col, Col1Id)
Clustered on (Col, Col1ID)
Any help on this is welcome.
Try adding included columns to the Table1 index and create a new index on Table2. Also, make sure statistics are up-to-date.
CREATE INDEX idx_Table1_Active_Date ON dbo.Table1 (Active, Date) INCLUDE (Col1Id, Col2Id);
CREATE INDEX idx_Table2_Col1ID_Active ON dbo.Table2 (Col1ID, Active);
I have this query that works but it is slow
SELECT
ID_NODE,
-- this case slows down the query!!!
CASE WHEN (EXISTS (SELECT MV.ID_CHILD FROM MYVIEW MV INNER JOIN MYTABLE1 MT1 ON MT1.ID_NODE = MV.ID_CHILD WHERE MV.ID_PARENT = CA.ID_NODE AND ID_FATHER IS NOT NULL)) THEN 'Y' ELSE 'N' END AS HAVE_CHILDREN,
OTHER_FIELDS
FROM
MYTABLE2
Update: After the first answer I realized my sample was not perfect, so i modified it doign 2 changes (CA to MT1, and writing MT1.ID_FATHER isntead of ID_FATHER)
SELECT
ID_NODE,
-- this case slows down the query!!!
CASE WHEN (EXISTS (SELECT MV.ID_CHILD FROM MYVIEW MV INNER JOIN MYTABLE1 MT1 ON MT1.ID_NODE = MV.ID_CHILD WHERE MV.ID_PARENT = MT2.ID_NODE AND MT1.ID_FATHER IS NOT NULL)) THEN 'Y' ELSE 'N' END AS HAVE_CHILDREN,
OTHER_FIELDS
FROM
MYTABLE2
End of update
Basically I want a 'y'/'n' result about "does this node have a child?
In execution plan i see only one warning that is:
Nested Loop (inner Join)) 43%
Could you please suggest an improvement to the query?
As exterme solution i can store in the table the HAVE_CHILDREN value as a new field, but i don't like this because it is an "highway to bugs".
Note for Bounty:
I post here the original tables, view (made with CREATE statements) and query to help provide a reply:
--This is MYTABLE1
CREATE TABLE [dbo].[MAN_PRG_OPERAZIONI](
[ID_PROG_OPERAZIONE] [int] NOT NULL,
[ID_CESPITE] [int] NOT NULL,
[ID_TIPO_OPERAZIONE] [int] NOT NULL,
[SEQUENZA] [int] NOT NULL,
[ID_RESPONSABILE] [int] NULL,
[DATA_SCADENZA] [datetime] NULL,
[DATA_ULTIMA] [datetime] NULL,
[ID_TIPO_FREQUENZA] [int] NOT NULL,
[FREQUENZA] [int] NOT NULL,
[NOTIFICA_SCADENZA] [nchar](1) NOT NULL,
[COSTO_FISSO] [numeric](19, 4) NOT NULL,
[NOTE] [nvarchar](max) NULL,
[ID_CONTO_FORNITORE] [int] NULL,
[ID_ESECUTORE] [int] NULL,
[GIORNI_INTERVENTO_PREVISTI] [int] NOT NULL,
[RIPETIZIONE] [nchar](1) NOT NULL,
[RIPETIZIONE_CONTINUA] [nchar](1) NOT NULL,
[RIPETI_FINO_A] [datetime] NULL,
[SOSPESO] [nchar](1) NOT NULL,
[ORE_LAVORO_PREVISTE] [decimal](8, 2) NOT NULL,
[DESCR_TITOLO_OPERAZIONE] [nvarchar](100) NOT NULL,
[ID_TEMPLATE] [int] NULL,
[TEMPLATE] [nvarchar](25) NULL,
[ID_PARENT_TEMPLATE_REMOTE] [int] NULL,
[ATTIVO] [nchar](1) NOT NULL,
[ID_PARENT_TEMPLATE] [int] NULL,
[NOTIFY_RESPONSIBLE] [nchar](1) NULL,
[NOTIFY_EXECUTOR] [nchar](1) NULL,
[NOTIFY_OTHERS] [nvarchar](200) NULL,
[NOTIFY_INADVANCE] [nchar](1) NULL,
[NOTIFY_ADVANCE_DAYS] [int] NULL,
[NOTIFY_ONEXECUTION] [nchar](1) NULL,
[NOTIFY_ONCLOSE] [nchar](1) NULL,
[ID_UTENTE_INS] [int] NULL,
[DATA_INS] [datetime] NULL,
[ID_UTENTE_ULT_MOD] [int] NULL,
[DATA_ULTIMA_MOD] [datetime] NULL,
[STATO_CKL] [int] NOT NULL,
[TAGAPPSYNC] [nchar](1) NOT NULL,
[IS_FATHER] [nchar](1) NOT NULL,
[ID_FATHER] [int] NULL,
[NOTIFY_DELAYS] [nchar](1) NULL,
[NOTIFY_DELAYS_DAYS] [int] NULL,
[NOTIFY_INS_USER] [nchar](1) NULL,
CONSTRAINT [PK_MAN_PRG_OPERAZIONI] PRIMARY KEY CLUSTERED
(
[ID_PROG_OPERAZIONE] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90) ON [PRIMARY]
) ON [PRIMARY]
--This is MYTABLE2
CREATE TABLE [dbo].[CES_ANAGRAFICA](
[ID_CESPITE] [int] NOT NULL,
[ID_CESPITE_PADRE] [int] NULL,
[COD_CESPITE] [nvarchar](50) NOT NULL,
[DESCR_CESPITE] [nvarchar](120) NOT NULL,
[IMMATERIALE] [nchar](1) NOT NULL,
[DATA_ACQUISTO] [datetime] NULL,
[DATA_ENTRATA_FUNZIONE] [datetime] NULL,
[DATA_DISMISSIONE] [datetime] NULL,
[BENE_USATO] [nchar](1) NOT NULL,
[ID_UBICAZIONE] [int] NULL,
[NRO_IDENTIFICAZIONE] [nvarchar](50) NULL,
[MARCA] [nvarchar](50) NULL,
[MODELLO] [nvarchar](50) NULL,
[MARCATURA_CE] [nchar](1) NULL,
[ANNO_COSTRUZIONE] [int] NULL,
[MATRICOLA_COSTRUTTORE] [nvarchar](50) NULL,
[COSTRUTTORE] [nvarchar](80) NULL,
[ID_CONTO_FORNITORE] [int] NULL,
[NOTE] [nvarchar](max) NULL,
[ID_TIPO_CESPITE] [int] NOT NULL,
[ID_STATO_CESPITE] [int] NULL,
[ID_CONTO_PROPRIETA] [int] NULL,
[ID_RESPONSABILE] [int] NULL,
[DATA_SCAD_GARANZIA] [datetime] NULL,
[CAMPO_MISURA] [nvarchar](80) NULL,
[CRITERI_ACC] [nvarchar](80) NULL,
[RISOLUZIONE] [nvarchar](80) NULL,
[ID_USO_STRUMENTO] [int] NULL,
[ID_REFERENTE] [int] NULL,
[FOTO] [varbinary](max) NULL,
[PROF_ID] [int] NOT NULL,
[ID_TEMPLATE] [int] NULL,
[TEMPLATE] [nvarchar](25) NULL,
[ID_PARENT_TEMPLATE_REMOTE] [int] NULL,
[ID_PARENT_TEMPLATE] [int] NULL,
[ISLOCKED] [nchar](1) NULL,
[TAGAPPSYNC] [nchar](1) NOT NULL,
[TAGAPPDOCSYNC] [nchar](1) NOT NULL,
CONSTRAINT [PK_CES_ANAGRAFICA] PRIMARY KEY CLUSTERED
(
[ID_CESPITE] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90) ON [PRIMARY]
) ON [PRIMARY]
--This is MYVIEW
CREATE VIEW [dbo].[V_CESPITE_TREE] AS
--BEGIN
WITH q AS
(
SELECT ID_CESPITE , ID_CESPITE AS ID_CESPITE_ANCESTOR
FROM CES_ANAGRAFICA c
JOIN CES_TIPI_CESPITE ctc ON ctc.ID_TIPI_INFRSTR = c.ID_TIPO_CESPITE
UNION ALL
SELECT c.ID_CESPITE, q.ID_CESPITE_ANCESTOR
FROM q
JOIN CES_ANAGRAFICA c
ON c.ID_CESPITE_PADRE = q.ID_CESPITE
JOIN CES_TIPI_CESPITE ctc ON ctc.ID_TIPI_INFRSTR = c.ID_TIPO_CESPITE
)
select ID_CESPITE AS ID_CHILD, ID_CESPITE_ANCESTOR AS ID_PARENT from q
GO
-- So my original query was this:
SELECT
CA.ID_CESPITE,CASE WHEN (EXISTS (SELECT VCA.ID_CHILD FROM V_CESPITE_TREE VCA INNER JOIN MAN_PRG_OPERAZIONI MPO ON MPO.ID_CESPITE = VCA.ID_CHILD WHERE VCA.ID_PARENT = CA.ID_CESPITE AND ID_FATHER IS NOT NULL)) THEN 'Y' ELSE 'N' END AS HAVE_CHILD_PRG,
CA.ID_CESPITE_PADRE
<Other Fields>
FROM
CES_ANAGRAFICA CA LEFT OUTER JOIN
CES_PERMESSI CP ON ((CA.ID_CESPITE = CP.ID_CESPITE)) INNER JOIN CES_TIPI_CESPITE CTCS ON CA.ID_TIPO_CESPITE = CTCS.ID_TIPI_INFRSTR LEFT OUTER JOIN
V_UTENTI_DIPENDENTI VUD ON CA.ID_RESPONSABILE = VUD.ID_DIPENDENTE
SELECT
CA.ID_CESPITE,
CASE WHEN child.[Count] > 0 THEN 'Y' ELSE 'N' END AS HAVE_CHILD_PRG,
CA.ID_CESPITE_PADRE
FROM
CES_ANAGRAFICA CA
LEFT OUTER JOIN CES_PERMESSI CP
ON ((CA.ID_CESPITE = CP.ID_CESPITE))
INNER JOIN CES_TIPI_CESPITE CTCS
ON CA.ID_TIPO_CESPITE = CTCS.ID_TIPI_INFRSTR
LEFT OUTER JOIN V_UTENTI_DIPENDENTI VUD
ON CA.ID_RESPONSABILE = VUD.ID_DIPENDENTE
CROSS APPLY (
SELECT [Count] = COUNT(*)
FROM V_CESPITE_TREE VCA
JOIN MAN_PRG_OPERAZIONI MPO
ON MPO.ID_CESPITE = VCA.ID_CHILD
WHERE VCA.ID_PARENT = CA.ID_CESPITE
AND MPO.ID_FATHER is not null
) child
This should perform slightly better since it doesn't have to perform an aggregate function.
SELECT
CA.ID_CESPITE,
CASE WHEN child.[Exists] = 1 THEN 'Y' ELSE 'N' END AS HAVE_CHILD_PRG,
CA.ID_CESPITE_PADRE
FROM
CES_ANAGRAFICA CA
LEFT OUTER JOIN CES_PERMESSI CP
ON ((CA.ID_CESPITE = CP.ID_CESPITE))
INNER JOIN CES_TIPI_CESPITE CTCS
ON CA.ID_TIPO_CESPITE = CTCS.ID_TIPI_INFRSTR
LEFT OUTER JOIN V_UTENTI_DIPENDENTI VUD
ON CA.ID_RESPONSABILE = VUD.ID_DIPENDENTE
OUTER APPLY (
SELECT TOP (1) 1 [Exists]
FROM V_CESPITE_TREE VCA
JOIN MAN_PRG_OPERAZIONI MPO
ON MPO.ID_CESPITE = VCA.ID_CHILD
WHERE VCA.ID_PARENT = CA.ID_CESPITE
AND MPO.ID_FATHER is not null
) child
Use a CTE ; it will load your inner join once and then cache it.
Note I don't know where the CA.ID_NODE comes from as you didn't explain that.
Also your inner query joins to MyTable1, but you're not relating MyTable2 and the subquery.
Given off what you've provided, the pseudo-code should be something like this:
(If you update your question with pertinent info I'll update this answer to reflect it).
Update:
Here's an updated version based on your schema update and some sample data. I confirmed this no longer causes duplicates. The real question is for you to check it and make sure it increases the performance, aka lowers execution time.
; with hasChildCte(ID_CESPITE, ID_PARENT)
As (
SELECT VCA.ID_CHILD,
vca.ID_PARENT
FROM V_CESPITE_TREE VCA
INNER JOIN MAN_PRG_OPERAZIONI MPO
ON MPO.ID_CESPITE = VCA.ID_CHILD
WHERE ID_FATHER
IS NOT NULL
)
Select
CA.ID_CESPITE,
Case
When Exists (
Select ID_PARENT
From hasChildCte cte
Where cte.ID_PARENT = ca.ID_CESPITE
) Then 'Y'
Else 'N'
End As HAVE_CHILDREN,
CA.ID_CESPITE_PADRE
From CES_ANAGRAFICA CA
Note also if you don't have indexes on all of the joined columns, it would be a good move to put those in. This will help to speed up the query further especially if you're working with a large amount of data.
Update 2
The comment about the CTE executing more than once got me thinking, and it is apparently up to SQL server to decide to cache a CTE or not, rather than always caching. In many cases CTE will execute only once, but other times it's similar to a view in SQL server and doesn't get cached.
Consequently, I've modified the code to use a table variable instead. I don't have enough test data to see which performs better or faster, though.
Try this and see if it yields a faster query execution time. Note also regardless of which method of refactoring and performance improvement you choose, it's a good idea to have your database properly setup with indexes on the columns you are using in JOIN's. This increases query execution time significantly, at the slight cost on inserts of the index having to be updated.
The updated, non-CTE, code, using a table variable instead:
Declare #HasChildren table (ID_CESPITE int, ID_PARENT int)
Insert into #HasChildren
Select VCA.ID_CHILD,
vca.ID_PARENT
From V_CESPITE_TREE VCA
Inner Join MAN_PRG_OPERAZIONI MPO
On MPO.ID_CESPITE = VCA.ID_CHILD
Where ID_FATHER
Is Not Null
Select
CA.ID_CESPITE,
Case
When Exists (
Select ID_PARENT
From #HasChildren c
Where c.ID_PARENT = ca.ID_CESPITE
) Then 'Y'
Else 'N'
End As HAVE_CHILDREN,
CA.ID_CESPITE_PADRE
From CES_ANAGRAFICA CA
Something like the below might be a little easier for the optimizer to handle.
SELECT DISTINCT
ID_NODE,
CASE WHEN MTSub.ID_CHILD is null then 'N' else 'Y' END,
OTHERFIELDS
FROM MYTABLE2
LEFT JOIN
(SELECT MV.ID_CHILD FROM MYVIEW MV INNER JOIN MYTABLE1 MT1 ON MT1.ID_NODE = MV.ID_CHILD WHERE MV.ID_PARENT = MT2.ID_NODE AND MT1.ID_FATHER IS NOT NULL) MTSub
Long time reader, first time caller, and all that...
Here's the business problem: a user makes one or more requests for documents. At some later time, a document is uploaded to the system. If that document matches one or more of the requests, the document request is fulfilled. So, there could be one or many requests for Document A, for instance. When Document A is uploaded, all of the requests for Document A are fulfilled.
And here's my technical problem: I've got an AFTER INSERT trigger on the table where the upload of the document is recorded. It checks the DocumentRequest table and updates each row that matches the uploaded document. If only one row matches (only one request for Document A), everything is hunky-dory. If, however, more than one matches, the UPDATE statement in the trigger fails -- I see this error:
Subquery returned more than 1 value.
This is not permitted when the
subquery follows =, !=, <, <= , >, >=
or when the subquery is used as an
expression.
Here are the CREATE statements for the relevant tables:
CREATE TABLE [dbo].[DocumentRequest](
[DocumentRequestId] [int] IDENTITY(1,1) NOT NULL,
[BatchID] [int] NULL,
[CertificateNum] [varchar](20) NOT NULL,
[RequestedTypCd] [varchar](5) NULL,
[RequestedReason] [varchar](60) NULL,
[DestinationTypCd] [varchar](5) NULL,
[DocumentPackageTypCd] [varchar](5) NULL,
[RequestedDtm] [datetime] NOT NULL,
[RequestNotes] [varchar](1000) NULL,
[RequestStatusTypCd] [varchar](5) NOT NULL,
[InactiveFlag] [char](1) NULL,
[CreationDtm] [datetime] NOT NULL,
[CreationUserID] [varchar](10) NOT NULL,
[CompletedDtm] [datetime] NULL,
[CompletedUserID] [varchar](10) NULL,
[ModifiedDtm] [datetime] NULL,
[ModifiedUserID] [varchar](10) NULL,
CONSTRAINT [XPKDocumentRequest] PRIMARY KEY NONCLUSTERED
(
[DocumentRequestId] ASC
)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
--------------
CREATE TABLE [dbo].[DocumentRequestContents](
[DocumentTypCd] [varchar](5) NOT NULL,
[CreationDtm] [datetime] NOT NULL,
[CreationUserID] [varchar](10) NOT NULL,
[DocumentRequestId] [int] NOT NULL,
[DocumentReceivedDtm] [datetime] NULL,
[DocumentReceivedFlag] [char](1) NULL,
[DocumentIgnoreFlag] [char](1) NULL,
CONSTRAINT [XPKDocumentRequestContents] PRIMARY KEY NONCLUSTERED
(
[DocumentRequestId] ASC,
[DocumentTypCd] ASC
)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
--------------
CREATE TABLE [dbo].[DocumentStorage](
[DocumentStorageID] [int] IDENTITY(1,1) NOT NULL,
[CertificateNum] [varchar](10) NULL,
[DocumentHandleID] [int] NULL,
[DocumentTypVal] [varchar](100) NULL,
[CreationDtm] [datetime] NULL,
[CreationUserID] [varchar](10) NULL,
[lastupd_user] [varchar](8) NULL,
[lastupd_stamp] [datetime] NULL,
[DocumentNam] [varchar](100) NULL,
PRIMARY KEY NONCLUSTERED
(
[DocumentStorageID] ASC
)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
--------------
CREATE TABLE [dbo].[DocumentProfile](
[DocumentProfileID] [int] IDENTITY(1,1) NOT NULL,
[DocumentTypCd] [varchar](5) NULL,
[DocumentVersionNum] [varchar](10) NULL,
[DocumentApproveInd] [varchar](1) NULL,
[CreationDtm] [datetime] NULL,
[CreationUserID] [varchar](10) NULL,
[lastupd_stamp] [datetime] NULL,
[lastupd_user] [varchar](8) NULL,
[DocumentStorageTypVal] [varchar](100) NULL,
[MIMETypVal] [varchar](50) NULL,
[DocumentSourceTypCd] [varchar](5) NULL,
[DocumentFormatTypCd] [varchar](5) NULL,
[DocumentReceiveLocationTypCd] [varchar](5) NULL,
[DocumentIndexTypCd] [varchar](5) NULL,
[DocumentNam] [varchar](100) NULL,
[DocumentTrackedInd] [char](1) NULL CONSTRAINT [DF_DocumentProfile_DocumentTrackedInd] DEFAULT ('N'),
PRIMARY KEY NONCLUSTERED
(
[DocumentProfileID] ASC
)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
And the trigger...
CREATE TRIGGER [trg_DMTDocumentReceived_DocumentStorage]
ON [dbo].[DocumentStorage]
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON;
UPDATE DocumentRequestContents
SET DocumentRequestContents.DocumentReceivedFlag = 'Y',
DocumentRequestContents.DocumentReceivedDtm = getdate()
FROM
DocumentRequestContents
INNER JOIN DocumentRequest
ON DocumentRequestContents.DocumentRequestId = DocumentRequest.DocumentRequestId
INNER JOIN DocumentProfile
ON DocumentRequestContents.DocumentTypCd = DocumentProfile.DocumentTypCd
INNER JOIN Inserted
ON DocumentProfile.DocumentStorageTypVal = Inserted.DocumentTypVal
WHERE
(DocumentRequestContents.DocumentReceivedFlag <> 'Y' OR
DocumentRequestContents.DocumentReceivedFlag IS NULL)
AND (DocumentRequest.InactiveFlag IS NULL)
AND (DocumentRequest.CertificateNum = Inserted.CertificateNum)
AND (DocumentProfile.DocumentStorageTypVal = Inserted.DocumentTypVal)
END
I'm at a bit of a loss as to where to go from here. Can you give a guy a hand?
EDIT:
A request can have more than one document, so the table that is being updated in the trigger (DocumentRequestContents) also has a trigger on it, which determines if the entire request has been fulfilled. As far as I can tell, it does not have a subquery, either, but here it is:
CREATE TRIGGER [trg_DMTDocumentReceived_CompletenessCheck]
ON [dbo].[DocumentRequestContents]
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON;
DECLARE #TotalDocs int, #ReceivedDocs int
IF UPDATE(DocumentReceivedFlag)
BEGIN
IF (SELECT DocumentReceivedFlag FROM Inserted) = 'Y' AND (SELECT DocumentReceivedFlag FROM Deleted) = 'N'
BEGIN
SELECT #TotalDocs = count(*)
FROM DocumentRequestContents
INNER JOIN Inserted
ON DocumentRequestContents.DocumentRequestID = Inserted.DocumentRequestID
WHERE (DocumentRequestContents.DocumentIgnoreFlag <> 'Y' OR DocumentRequestContents.DocumentIgnoreFlag IS NULL)
SELECT #ReceivedDocs = count(*)
FROM DocumentRequestContents
INNER JOIN Inserted
ON DocumentRequestContents.DocumentRequestID = Inserted.DocumentRequestID
WHERE DocumentRequestContents.DocumentReceivedFlag = 'Y'
IF (#ReceivedDocs = #TotalDocs)
BEGIN
UPDATE DocumentRequest
SET RequestStatusTypCd = 'CMPLT',
CompletedDtm = getdate(),
CompletedUserID = 'SYSTEM',
ModifiedDtm = getdate(),
ModifiedUserID = 'SYSTEM'
FROM DocumentRequest
INNER JOIN Inserted
ON DocumentRequest.DocumentRequestId = Inserted.DocumentRequestId
END
END
END
END
Thanks,
Jason
The offending line appears to be here:
IF (SELECT DocumentReceivedFlag FROM Inserted) = 'Y' AND
(SELECT DocumentReceivedFlag FROM Deleted) = 'N' ...
This is essentially a "subquery". The IF statement is written with the expectation that there will only be one row in both inserted and deleted. Without knowing the details of what the business logic is here, I can only speculate, but most likely you'll be OK if you just rewrite that as:
IF EXISTS(SELECT 1 FROM inserted WHERE DocumentReceivedFlag = 'Y') AND
EXISTS(SELECT 1 FROM deleted WHERE DocumentReceivedFlag = 'N'))
But it might be more complicated than that... it could be that you actually need to join the inserted to deleted rows and check if the flag has actually changed from 'Y' to 'N'.
Check if you don't have a trigger on DocumentRequestContents as well.
On re-reading, the obvious problem is this part of the second trigger:
IF (SELECT DocumentReceivedFlag FROM Inserted) = 'Y'
AND (SELECT DocumentReceivedFlag FROM Deleted) = 'N'
This would fail if multiple rows where inserted, because SQL Server expects both of these subqueries to return one or no rows.
By the way, I personally avoid triggers at any cost. They introduce too much complexity for me to handle.
You may want to try aliasing the Inserted table in your join. While I haven't experienced this myself, it may be attempting to perform a subquery in your WHERE clause rather than using the joined table.
Your trigger doesn't appear to have a sub-query anywhere in it - this is the only trigger on your table? This error isn't caused by a join or a multi-row update, so I'm at a loss as to why your trigger would be generating this error.
Do you get the error when you're attempting the insert into the table itself? Perhaps it's actually the query you're using to insert that's failing - please post that insert statement as well.