I have never interacted with Hash Join yet.
According to picture below, do I need an index on table tblFin_Invoices?
Or which table do I need to create index in order to avoid this Hash Join?
Is any chance I can do something to alleviate the query?
------------------------------------------------------------------------
declare
#EffDateFrom datetime = '2017-02-01',
#EffDateTo datetime= '2017-12-31'
DECLARE #ValidInvoicesTable TABLE (InvoiceNum INT PRIMARY KEY)
INSERT INTO #ValidInvoicesTable
SELECT DISTINCT INV.InvoiceNum
FROM tblFin_Invoices INV
INNER JOIN tblQuotes ON INV.QuoteID = tblQuotes.QuoteID
--INNER JOIN tblClientOffices ON tblQuotes.QuotingLocationGuid = tblClientOffices.OfficeGUID
WHERE (INV.Failed = 0)
AND dateDiff(d, #EffDateFrom, dbo.tblQuotes.EffectiveDate) >= 0
AND dateDiff(d, #EffDateTo, dbo.tblQuotes.EffectiveDate) <= 0
AND dbo.tblQuotes.LineGUID = '6E00868B-FFC3-4CA0-876F-CC258F1ED22D'
DECLARE #TempData TABLE(
[QuoteID] int,
[QuoteGUID] [uniqueidentifier] NOT NULL,
[CompanyLocationGuid] [uniqueidentifier] NULL,
[UnderwriterUserGuid] [uniqueidentifier] NULL,
[InsuredGuid] [uniqueidentifier] NULL,
[ProducerGuid] [uniqueidentifier] NULL,
[ProducerContactGuid] [uniqueidentifier] NULL,
[EffectiveDate] datetime NULL,
--[InvoiceDate] datetime NULL,
[AccountingDate] datetime NULL,
[PolicyTypeID] tinyint NOT NULL, --------------
[TransactionTypeID] varchar(2) NULL,
[QuoteStatusID] tinyint NOT NULL,
[PolicyNumber] [varchar](150) NULL,
[StateID] [char](2) NULL,
[Premium] [money] NULL,
BondRate decimal (5,4) NULL,
[PenalLiability] [money] NULL,
[CompanyCommission] decimal(3,2) NULL
)
INSERT INTO #TempData
SELECT
INV.QuoteID,
tblQuotes.QuoteGUID,
tblQuotes.CompanyLocationGuid,
tblQuotes.UnderwriterUserGuid,
tblSubmissionGroup.InsuredGuid,
tblProducerLocations.ProducerGUID,
tblQuotes.ProducerContactGuid,
INV.EffectiveDate,
INV.InvoiceDate,
--dbo.CalcAccountingDate(tblQuotes.QuoteStatusID,INV.invoicedate,INV.effectivedate, tblQuotes.EndorsementEffective) AccountingDate,
tblQuotes.PolicyTypeID,
tblQuotes.TransactionTypeID,
tblQuotes.QuoteStatusID,
tblQuotes.PolicyNumber,
tblQuotes.StateID,
(SELECT ISNULL(SUM(tblFin_InvoiceDetails.AmtBilled), 0)
FROM tblFin_InvoiceDetails
WHERE (tblFin_InvoiceDetails.ChargeType = 'P')
AND (tblFin_InvoiceDetails.InvoiceNum = INV.InvoiceNum))
AS Premium,
[Dynamic_Data_SuretyPRC].BondRate,
[Dynamic_Data_SuretyPRC].BondAmount,
[Dynamic_Data_SuretyPRC].CompanyComm
FROM tblFin_Invoices INV
INNER JOIN tblQuotes ON INV.QuoteID = tblQuotes.QuoteID
INNER JOIN tblProducerLocations ON INV.ProducerLocationGUID = tblProducerLocations.ProducerLocationGUID
INNER JOIN tblSubmissionGroup ON tblQuotes.SubmissionGroupGuid = tblSubmissionGroup.SubmissionGroupGUID
LEFT JOIN [dbo].[Dynamic_Data_SuretyPRC] ON Dynamic_Data_SuretyPRC.QuoteGUID = tblQuotes.QuoteGUID
WHERE INV.InvoiceNum IN (SELECT * FROM #ValidInvoicesTable)
ORDER BY INV.InvoiceDate
--select * from #TempData
All the tables you use in the first query for #ValidInvoicesTable are also in the second query. Instead of using #ValidInvoicesTable, use the WHERE conditions from that SELECT and replace the WHERE condition in the second SELECT statement.
I'd also use BETWEEN instead of two statements for the date comparison, to get the same result you'll need to CAST/CONVERT the EffectiveDate, if the DateTime field actually contains Time.
I'd also make a join out of tblFin_InvoiceDetails.
I assume that you are going to use the #TempData table for something other the just a SELECT statement. If I'm wrong, you should remove the DECLARE and INSERT INTO regarding #TempData table completely.
Below is my suggestion.
declare
#EffDateFrom datetime = '2017-02-01',
#EffDateTo datetime= '2017-12-31'
DECLARE #TempData TABLE(
[QuoteID] int,
[QuoteGUID] [uniqueidentifier] NOT NULL,
[CompanyLocationGuid] [uniqueidentifier] NULL,
[UnderwriterUserGuid] [uniqueidentifier] NULL,
[InsuredGuid] [uniqueidentifier] NULL,
[ProducerGuid] [uniqueidentifier] NULL,
[ProducerContactGuid] [uniqueidentifier] NULL,
[EffectiveDate] datetime NULL,
--[InvoiceDate] datetime NULL,
[AccountingDate] datetime NULL,
[PolicyTypeID] tinyint NOT NULL, --------------
[TransactionTypeID] varchar(2) NULL,
[QuoteStatusID] tinyint NOT NULL,
[PolicyNumber] [varchar](150) NULL,
[StateID] [char](2) NULL,
[Premium] [money] NULL,
BondRate decimal (5,4) NULL,
[PenalLiability] [money] NULL,
[CompanyCommission] decimal(3,2) NULL
)
INSERT INTO #TempData
SELECT
INV.QuoteID,
tblQuotes.QuoteGUID,
tblQuotes.CompanyLocationGuid,
tblQuotes.UnderwriterUserGuid,
tblSubmissionGroup.InsuredGuid,
tblProducerLocations.ProducerGUID,
tblQuotes.ProducerContactGuid,
INV.EffectiveDate,
INV.InvoiceDate,
tblQuotes.PolicyTypeID,
tblQuotes.TransactionTypeID,
tblQuotes.QuoteStatusID,
tblQuotes.PolicyNumber,
tblQuotes.StateID,
SUM(ISNULL(tblDetailed.AmtBilled, 0)) AS Premium,
[Dynamic_Data_SuretyPRC].BondRate,
[Dynamic_Data_SuretyPRC].BondAmount,
[Dynamic_Data_SuretyPRC].CompanyComm
FROM tblFin_Invoices INV
INNER JOIN tblQuotes ON INV.QuoteID = tblQuotes.QuoteID
INNER JOIN tblProducerLocations ON INV.ProducerLocationGUID = tblProducerLocations.ProducerLocationGUID
INNER JOIN tblSubmissionGroup ON tblQuotes.SubmissionGroupGuid = tblSubmissionGroup.SubmissionGroupGUID
LEFT JOIN [dbo].[Dynamic_Data_SuretyPRC] ON Dynamic_Data_SuretyPRC.QuoteGUID = tblQuotes.QuoteGUID
LEFT JOIN tblFin_InvoiceDetail tblDetail ON tblDetail.InvoiceNum = INV.InvoiceNum AND tblDetail.ChargeType = 'P'
WHERE
INV.Failed = 0
AND CAST(dbo.tblQuotes.EffectiveDate as date) BETWEEN #EffDateFrom AND #EffDateTo
AND dbo.tblQuotes.LineGUID = '6E00868B-FFC3-4CA0-876F-CC258F1ED22D'
GROUP BY INV.QuoteID,
tblQuotes.QuoteGUID,
tblQuotes.CompanyLocationGuid,
tblQuotes.UnderwriterUserGuid,
tblSubmissionGroup.InsuredGuid,
tblProducerLocations.ProducerGUID,
tblQuotes.ProducerContactGuid,
INV.EffectiveDate,
INV.InvoiceDate,
tblQuotes.PolicyTypeID,
tblQuotes.TransactionTypeID,
tblQuotes.QuoteStatusID,
tblQuotes.PolicyNumber,
tblQuotes.StateID,
[Dynamic_Data_SuretyPRC].BondRate,
[Dynamic_Data_SuretyPRC].BondAmount,
[Dynamic_Data_SuretyPRC].CompanyComm
ORDER BY INV.InvoiceDate
Related
I want to import data from this SQL table:
CREATE TABLE [dbo].[TempExchangeRates](
[currency (Libellés)] [nvarchar](255) NULL,
[Currency Code] [nvarchar](255) NULL,
[2019-03] [float] NULL,
[2019-04] [float] NULL,
[2019-05] [float] NULL,
[2019-06] [float] NULL,
[2019-07] [float] NULL,
[2019-08] [float] NULL,
[2019-09] [float] NULL,
[2019-10] [float] NULL,
[2019-11] [float] NULL,
[2019-12] [float] NULL,
[2020-01] [float] NULL,
[2020-02] [float] NULL
)
With sample data:
To this one:
CREATE TABLE [dbo].[ExchangeRates]
(
[IdExchangeRate] [uniqueidentifier] NOT NULL,
[ExchangeRateCode] [nvarchar](10) NULL,
[ExchangeRatePeriodStartDate] [datetime] NULL,
[ExchangeRatePeriodEndDate] [datetime] NULL,
[ExchangeRateValue] [decimal](20, 5) NULL,
[CurrencyCode] [nvarchar](10) NULL,
)
Now I want to call a stored procedure to get fill the real table like that:
I start with stored procedure like that but I'm not sure how I could do that
------------------------- 3. Declare StartDateTable --------------------
DECLARE #StartDateExchangeRate TABLE
(
rowid INT IDENTITY(1,1) NOT NULL,
value float,
startDate date
)
-- Insert Into #StartDateExchangeRate(value, startDate)
--This finds the start dates by finding unmatched values
--SELECT id,value
-- from ExchangeRates
------------------------- 2. Declare EndDateTable --------------------
DECLARE #EndDateExchangeRate TABLE
(
EndDate date
)
Insert Into #ENdDateExchangeRate(EndDate)
--This finds the start dates by finding unmatched values
SELECT EOMONTH(startdate)
FROM #StartDateExchangeRate As ER1
-------------------------3. Join NotYet--------------------------
This question is lacking in details
Assuming the TempExchangeRates columns will vary as time goes on, here is a option that will dynamically UNPIVOT the data so it can be inserted into your final structure.
Example (or dbFiddle)
Select ExchangeRateCode = A.[Currency Code]
,ExchangeRatePeriodStartDate = period
,ExchangeRatePeriodEndDate = EOMonth(period)
,ExchangeRateValue = B.Value
,CurrencyCode = replace(upper(A.[currency (Libellés)]),' ','')
,CreatedBy = 'SomeString'
,CreatededAt = getdate()
From [TempExchangeRates] A
Cross Apply ( Select period = try_convert(date,[Key]+'-01')
,Value = try_convert(float,value)
From OpenJson((Select A.* For JSON Path,Without_Array_Wrapper ))
Where [Key] not in ('currency (Libellés)','Currency Code')
) B
Returns
This question already has an answer here:
operation not allowed when the object is closed when running more advanced query
(1 answer)
Closed 6 years ago.
I am trying to query a SQL Server 2008 instance from VBScript.
I know my connection is working because when I use a simple query such as the one below it works fine.
sfQuery2 = "SELECT TOP 10 * FROM [DB].[schema].[table]"
The one thing I am unsure about is the user that I am connected as has read only rights. Not sure if the userid will be able to create temp tables if they are read only. I did run the same query from SQL Server with this user and the query worked. But when I try to run this with the same user from vbscript when I try to read the record set the error I get is.....
ADODB.Recordset: Operation is not allowed when the object is closed.
sfQuery2 = "CREATE TABLE #Temp1 ([LOGNAME] [nvarchar](20) NULL, [MESSAGE_TYPE] [int] NULL, [COMPONENT] [nvarchar](50) NULL, [LOGTIME] [nvarchar](17) NOT NULL, [SUB_SYSTEM] [nvarchar](40) NULL, [STACK_ID] [nvarchar](120) NULL, [SUBSTACK_ID] [int] NULL, [MESSAGE] [nvarchar](1800) NULL, [DETAIL] [nvarchar](1800) NULL ) INSERT INTO #Temp1 SELECT DISTINCT [LOGNAME], [MESSAGE_TYPE], [COMPONENT], LOGTIME, [SUB_SYSTEM], [STACK_ID], [SUBSTACK_ID], [MESSAGE], [DETAIL] FROM [DB].[schema].[table] WHERE cast(LEFT(LOGTIME,8) as date) = cast(getdate() -1 as date) AND [MESSAGE] LIKE '%Exporter->Archive' CREATE TABLE #Temp2 ([LOGNAME] [nvarchar](20) NULL, [MESSAGE_TYPE] [int] NULL, [COMPONENT] [nvarchar](50) NULL, [LOGTIME] [nvarchar](17) NOT NULL, [SUB_SYSTEM] [nvarchar](40) NULL, [STACK_ID] [nvarchar](120) NULL, [SUBSTACK_ID] [int] NULL, [MESSAGE] [nvarchar](1800) NULL, [DETAIL] [nvarchar](1800) NULL ) INSERT INTO #Temp2 SELECT DISTINCT [LOGNAME], [MESSAGE_TYPE], [COMPONENT],cast(LEFT(LOGTIME,8) as date) AS YesterdayDate, [SUB_SYSTEM], [STACK_ID], [SUBSTACK_ID], [MESSAGE], [DETAIL] FROM [DB].[schema].[table] WHERE cast(LEFT(LOGTIME,8) as date) = cast(getdate() -1 as date) AND [DETAIL] LIKE 'USER:%' SELECT * FROM ( SELECT RIGHT(b.DETAIL, 7) AS AXAID, cast(LEFT(a.LOGTIME,8) as date) AS [DATE], b.STACK_ID, ROW_NUMBER() OVER(PARTITION by b.STACK_ID ORDER BY a.LOGTIME DESC) rn FROM #Temp1 AS a INNER JOIN #Temp2 AS b ON a.STACK_ID = b.STACK_ID WHERE a.[MESSAGE] LIKE '%Exporter->Archive' ) a WHERE rn = 1 "
oSfRs.Open sfQuery2, oSfCn
if osfrs.BOF then
Debug "There are NO results"
else
Debug "There is data in the Record Set"
end if
I am not sure if it is a syntax error with my SQL or if there it possibly because the rights of the user. Any help would be appreciate.
Please let me know if more information is needed.
For those struggling to read the SQL here is a formatted version
CREATE TABLE #Temp1 (
[LOGNAME] [nvarchar](20) NULL,
[MESSAGE_TYPE] [int] NULL,
[COMPONENT] [nvarchar](50) NULL,
[LOGTIME] [nvarchar](17) NOT NULL,
[SUB_SYSTEM] [nvarchar](40) NULL,
[STACK_ID] [nvarchar](120) NULL,
[SUBSTACK_ID] [int] NULL,
[MESSAGE] [nvarchar](1800) NULL,
[DETAIL] [nvarchar](1800) NULL
)
INSERT INTO #Temp1
SELECT DISTINCT [LOGNAME], [MESSAGE_TYPE], [COMPONENT], LOGTIME, [SUB_SYSTEM]
, [STACK_ID], [SUBSTACK_ID], [MESSAGE], [DETAIL]
FROM [DB].[schema].[table]
WHERE cast(LEFT(LOGTIME,8) as date) = cast(getdate() -1 as date)
AND [MESSAGE] LIKE '%Exporter->Archive'
CREATE TABLE #Temp2 (
[LOGNAME] [nvarchar](20) NULL,
[MESSAGE_TYPE] [int] NULL,
[COMPONENT] [nvarchar](50) NULL,
[LOGTIME] [nvarchar](17) NOT NULL,
[SUB_SYSTEM] [nvarchar](40) NULL,
[STACK_ID] [nvarchar](120) NULL,
[SUBSTACK_ID] [int] NULL,
[MESSAGE] [nvarchar](1800) NULL,
[DETAIL] [nvarchar](1800) NULL
)
INSERT INTO #Temp2 SELECT DISTINCT [LOGNAME], [MESSAGE_TYPE], [COMPONENT]
, cast(LEFT(LOGTIME,8) as date) AS YesterdayDate
, [SUB_SYSTEM], [STACK_ID], [SUBSTACK_ID], [MESSAGE], [DETAIL]
FROM [DB].[schema].[table]
WHERE cast(LEFT(LOGTIME,8) as date) = cast(getdate() -1 as date)
AND [DETAIL] LIKE 'USER:%'
SELECT *
FROM (
SELECT RIGHT(b.DETAIL, 7) AS AXAID, cast(LEFT(a.LOGTIME,8) as date) AS [DATE]
, b.STACK_ID, ROW_NUMBER() OVER(PARTITION by b.STACK_ID ORDER BY a.LOGTIME DESC) rn
FROM #Temp1 AS a
INNER JOIN #Temp2 AS b ON a.STACK_ID = b.STACK_ID
WHERE a.[MESSAGE] LIKE '%Exporter->Archive'
) a WHERE rn = 1
Resolved by comment by Lankymart.
The resolution was to add SET NOCOUNT ON to the beginning of my query.
I'm a rookie to SQL Server. I'm using SQL Server 2008 R2. I have created two tables called adding_hanger and allot as follows
CREATE TABLE [dbo].[adding_hanger]
(
[End_Id] [bigint] IDENTITY(1,1) NOT NULL,
[Hanger_Location] [char](10) NOT NULL,
[Hanger_Capacity] [int] NOT NULL,
[Hanger_Id] AS ((CONVERT([varchar](100),substring([Hanger_Location],(1),(3)),0)+'101')+CONVERT([varchar](100),[End_Id],0)) PERSISTED NOT NULL,
[Manager_Name] [varchar](15) NOT NULL,
[Manager_Id] AS ((CONVERT([varchar](4),substring([Social_Security_No],(8),(4)),0)+'31')+CONVERT([varchar](100),[End_Id],0)) PERSISTED NOT NULL,
[Manager_Password] AS ((CONVERT([varchar](3),substring([Manager_Name],(1),(3)),0)+'#')+CONVERT([varchar](3),substring([Hanger_Location],(1),(3)),0)),
[Social_Security_No] [varchar](15) NOT NULL,
[Date_of_Birth] [datetime] NOT NULL,
[Gender] [varchar](8) NOT NULL,
[Mobile_No] [varchar](50) NOT NULL,
[Email_Address] [varchar](30) NOT NULL,
[House_No] [varchar](10) NOT NULL,
[Address_Line_1] [varchar](30) NOT NULL,
[Address_id] AS ((CONVERT([varchar](100),substring([City],(1),(3)),0)+'31')+CONVERT([varchar](100),[End_Id],0)) PERSISTED NOT NULL,
[City] [char](15) NOT NULL,
[State] [char](15) NOT NULL,
[Country] [char](15) NOT NULL,
[Pin_No] [int] NOT NULL
)
CREATE TABLE [dbo].[allot]
(
[Fromdate] [datetime] NULL,
[todate] [datetime] NULL,
[hangarlocation] [char](10) NULL,
[hangarno] [varchar](100) NULL,
[planeid] [varchar](100) NULL
)
How do I fetch the details of the available hangers that are not allotted within a given range of from and to dates given as input and also not exceeding the capacity of the hanger if allocated already?
First, your spelling of 'hangar' is inconsistent and should be addressed.
I'm assuming that the hangar capacity represents how many allots you can store in that hangar? If so, the hangar capacity might vary over a given timeframe as allots are added/removed (according to FromDate and ToDate), so your question isn't clear. If you want a hangar that definitely has a space free over a given period, I would use:
declare #from_date datetime, #end_date datetime, #i int
-- Set your start and end dates (replace the dates below with the correct ones)
set #from_date = '2016-07-01'
set #to_date = '2016-07-31'
set #i = 1 -- counter
-- Create a table of dates
select #from_date as date into #dates
while dateadd(day,#i,#from_date) <= #to_date
begin
insert into #dates values (dateadd(day,#i,#from_date))
end
-- Create table of date, hangar_id, hangar_capacity
select date, hangar_id, hangar_capacity
into #dates_by_hangar
from adding_hangar
inner join #dates on 1 = 1
-- Table showing hangars with at least one free space on every date in the range
select hangar_id from (
-- 2. Table showing hangar space free on each date
select a.*, a.hanger_capacity - isnull(b.allots_in_hangar,0) as hangar_space_free
from #dates_by_hangar a
left outer join (
-- 1. Table showing number of allots in each hangar on a given date
select date, hangar_no, sum(case when b.hangar_no is not null then 1 else 0 end) as allots_in_hangar
from #dates a
left outer join allot b
on a.date between b.fromdate and b.todate
group by date, hangar_no) b
on a.hangar_id = b.hangar_no
and a.date = b.date
)
group by hangar_no
having max(hangar_space_free) >= 1
SELECT ah.* from [adding_hanger] AS ah
INNER JOIN [allot] ON allot.hangarlocation = ah.Hanger_Location
WHERE (allot.Fromdate < <input_from_date> OR allot.todate > <input_to_date>)
OR (allot.Fromdate >= <input_from_date> AND allot.todate <= <input_to_date> AND ah.Hanger_Capacity <= <input_capacity>)
I have big log4net database table with this structure.
CREATE TABLE [dbo].[Log]
(
[Id] [INT] IDENTITY(1,1) NOT NULL,
[Date] [DATETIME] NOT NULL,
[Thread] [VARCHAR](255) NOT NULL,
[Level] [VARCHAR](50) NOT NULL,
[Logger] [VARCHAR](255) NOT NULL,
[Application] [VARCHAR](50) NOT NULL,
[Server] [VARCHAR](50) NOT NULL,
[Message] [VARCHAR](4000) NOT NULL,
[Exception] [TEXT] NULL,
[UserName] [NVARCHAR](50) NULL,
[CorrelationId] [VARCHAR](255) NULL,
CONSTRAINT [PK_Log] PRIMARY KEY CLUSTERED
(
[Id] ASC
)
But this table has cca 250 000 000 rows. I need return count for table. Standard way is (generated by entity framework )
SELECT [GroupBy1].[A1] AS [C1]
FROM (SELECT COUNT(1) AS [A1]
FROM [dbo].[Log] AS [Extent1]
WHERE ((([Extent1].[Level] = 'DEBUG')
AND ('DEBUG' IS NOT NULL))
OR (([Extent1].[Level] = 'INFO')
AND ('INFO' IS NOT NULL))
OR (([Extent1].[Level] = 'WARN')
AND ('WARN' IS NOT NULL)))
OR (([Extent1].[Logger] = 'PlusService')
AND ('PlusService' IS NOT NULL))) AS [GroupBy1]
but is very slow, average 5 minutes with litle where. I need using where clausule. Is there possible how i can using system count methods like this ?
SELECT CONVERT(bigint, rows)
FROM sysindexes
WHERE id = OBJECT_ID('Log')
AND indid < 2
or this
SELECT CAST(p.rows AS float)
FROM sys.tables AS tbl
INNER JOIN sys.indexes AS idx ON idx.object_id = tbl.object_id and idx.index_id < 2
INNER JOIN sys.partitions AS p ON p.object_id=CAST(tbl.object_id AS int)
AND p.index_id=idx.index_id
WHERE ((tbl.name=N'Log'
AND SCHEMA_NAME(tbl.schema_id)='dbo'))
or this
SELECT SUM (row_count)
FROM sys.dm_db_partition_stats
WHERE object_id=OBJECT_ID('Log')
AND (index_id=0 or index_id=1);
These three options I mentioned above are very fast. But I think it does not support using where clause. Is there any way that I can use methods like this?
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