SQL Server MERGE with conditions not merging - sql-server

I'm trying to do a simple update of my table when the collector has different data than the final table. I'm using the code below. When the first SELECT runs it tells me that the two columns both have data in them. After the MERGE statement runs, and I do the new SELECT, both of the columns are still showing as NULL. The merge isn't updating anything.
Have I messed up the MERGE syntax somehow?
The production code I have also includes WHEN NOT MATCHED and other items. This is just a small example to demonstrate the problem.
SELECT l.Region_Name, l.Region_Code
FROM dbo.Collector_Locations l
WHERE l.id = 1038
MERGE INTO dbo.Locations AS T
USING dbo.Collector_Locations S ON T.id = S.id
WHEN MATCHED AND (T.Region_Name <> S.Region_Name OR T.Region_Code <> S.Region_Code)
THEN
UPDATE SET T.Region_Name = S.Region_Name,
T.Region_Code = S.Region_Code;
SELECT l.Region_Name, l.Region_Code
FROM dbo.Locations l
WHERE l.id = 1038

Changing the query to accomodate NULL values:
MERGE dbo.Locations AS T
USING dbo.Collector_Locations S
ON T.id = S.id
WHEN MATCHED AND (ISNULL(T.Region_Name,'a') <> S.Region_Name OR ISNULL(T.Region_Code,'a') <> S.Region_Code) THEN UPDATE SET
T.Region_Name = S.Region_Name,
T.Region_Code = S.Region_Code;
Apart from that, the syntax looks fine. This should do the trick.
EDIT:
I would however go for a UPDATE statement in this scenario:
UPDATE dbo.Locations
SET T.Region_Name = S.Region_Name,T.Region_Code = S.Region_Code
FROM dbo.Locations T
INNER JOIN dbo.Collector_Locations S
ON T.id = S.id AND (T.Region_Name <> S.Region_Name OR T.Region_Code <> S.Region_Code)
You could also take our the last set of join condition, since the expected end result is going to be the same:
UPDATE dbo.Locations
SET T.Region_Name = S.Region_Name,T.Region_Code = S.Region_Code
FROM dbo.Locations T
INNER JOIN dbo.Collector_Locations S
ON T.id = S.id

Related

Multiple INNER JOIN SELECT query not returning data

This is a stored procedure to select the full details of a wine.
It takes as a parameter a storage location ID.
This builds properly and is similar in structure to many sp's I have written that work,
however it returns no data. Is there something I am missing?
CREATE PROCEDURE [sp_retrieveInventory_Full]
(
#StorageLocationID [int]
)
AS
BEGIN
SELECT [WineName],
[RackID],
[RackColumn],
[RackRow],
[WineTypeID],
[WineVarietyID],
[VintnerName],
[dbo].[Vintner].[Country],
[dbo].[Vintner].[StateProvince],
[dbo].[Vintner].[Region],
[VendorName],
[Vintage],
[PurchaseDate],
[PurchasePrice],
[BottleSizeID],
[ABV],
[DrinkByDate],
[FoodType],
[TastingNotes],
[RatedBy],
[RatingScore]
FROM [dbo].[StorageLocation]
INNER JOIN [dbo].[Wine]
ON [dbo].[StorageLocation].[WineID] = [dbo].[Wine].[WineID]
INNER JOIN [dbo].[Vintner]
ON [dbo].[Vintner].[VintnerID] = [dbo].[Wine].[VintnerID]
INNER JOIN [dbo].[Vendor]
ON [dbo].[Vendor].[VendorID] = [dbo].[Wine].[VendorID]
INNER JOIN [dbo].[Pairing]
ON [dbo].[Pairing].[PairingID] = [dbo].[Wine].[PairingID]
INNER JOIN [dbo].[Rating]
ON [dbo].[Rating].[RatingID] = [dbo].[Wine].[RatingID]
WHERE [dbo].[StorageLocation].[StorageLocationID] = #storageLocationID
END
GO
When a query reruns no result it's because no records exists that satisfy the conditions in it's where clause, or, in case of queries that contains inner joins, at least one of the joins on condition returns no records.
The easiest way to figure out what join is messing up your query is to start removing the joins one by one, but to do that, you have to first change the select clause.
What I like to do is this: Start with all the joins except the last one - see if it returns any records:
SELECT 1
FROM [dbo].[StorageLocation]
INNER JOIN [dbo].[Wine]
ON [dbo].[StorageLocation].[WineID] = [dbo].[Wine].[WineID]
INNER JOIN [dbo].[Vintner]
ON [dbo].[Vintner].[VintnerID] = [dbo].[Wine].[VintnerID]
INNER JOIN [dbo].[Vendor]
ON [dbo].[Vendor].[VendorID] = [dbo].[Wine].[VendorID]
INNER JOIN [dbo].[Pairing]
ON [dbo].[Pairing].[PairingID] = [dbo].[Wine].[PairingID]
-- INNER JOIN [dbo].[Rating]
-- ON [dbo].[Rating].[RatingID] = [dbo].[Wine].[RatingID]
WHERE [dbo].[StorageLocation].[StorageLocationID] = #storageLocationID
If not, comment out the next join and run the query again. Rinse and repeat.
Please note, however, that there might be more than one inner join that prevents the query from returning records.

Query very slow with bit fields and full text search

I have a strange problem, if I execute the following query, Sql Server (2008 and 2016) needs about 35 seconds to complete.
SELECT DISTINCT F.FilterTitle, F.FilterSlug, F.FilterOrderList, F.FilterType
FROM products p
JOIN products_translations pt ON pt.ProductId = p.Id AND pt.Culture = 'it'
JOIN facets F ON F.ProductId = p.Id AND F.Culture = 'it' AND F.FilterType = 2
JOIN products_categories pc ON pc.productId = p.id
JOIN categories C ON pc.CategoryId = C.Id
JOIN categories_slugs cps ON cps.CategoryId = C.ParentCategoryId AND cps.Culture = 'it'
JOIN categories_slugs cs ON cs.CategoryId = C.Id AND cs.Culture = 'it'
WHERE cps.value = 'string1' AND cs.value = 'string2'
AND CONTAINS(pt.DescriptionForSearch, '"value*"', LANGUAGE 1040) --this is a full text
AND p.Visible = 1 AND p.Payments = 0 --both are bit fields
ORDER BY F.FilterType, F.FilterOrderList, F.FilterTitle;
If I comment the line
and p.Visible = 1 and p.Payments = 0
the query needs just a couple of ms. The same if I remove the line
and contains(pt.DescriptionForSearch, '"value*"' , language 1040)
and leave the other statement intact. I also created two indexes for both the bit fields without changes.
Can someone shed some light on why this is happening?
UPDATE
At this link the two execution plans that I forgot to include.

IS NULL being ignored

I am trying to run a query in T-SQL to pull back a data set based on a column being null.
This is a simplified version of the code:
SELECT
T1.Col1, T1.Col2,
T1.Col3, T1.Col4
FROM
table1 AS T1
INNER JOIN
table2 AS T2 ON T1.Col2 = T2.Col3
WHERE
T2.Col4 IS NULL
Problem is, the result includes rows where T2.Col4 are NULL and also not NULL, it's like the WHERE clause doesn't exist.
Any ideas would be greatly
UPDATE - full version of code:
SELECT
M.ref
,C.cname
,CL.clname
,C.ccity
,M.productLine
,M.code
,CL.date
,M.dept
,DPT.group
,TK2.tkname
,TK2.tkdept
FROM DB.dbo.manage AS M
OUTER JOIN DB.dbo.ClientManageRelationship AS CMR
ON CMR.RelatedEntityID = M.EntityID
OUTER JOIN DB.dbo.Client AS C
ON C.EntityID = CMR.EntityID
INNER JOIN DB.dbo.ManageCustomerRelationship AS MCR
ON MCR.EntityID = M.EntityID
INNER JOIN DB.dbo.Customer AS CL
ON CL.EntityID = MCR.RelatedID
INNER JOIN DB.dbo.timek AS TK
ON TK.tki = M.tkid
LEFT JOIN (SELECT Group = division, [Department] = newdesc, deptcode FROM DB.csrt.vw_rep_p_l_dept) AS DPT
ON tkdept = DPT.dept
LEFT JOIN (SELECT Name = TK2.tkfirst + ' ' + TK2.tklast, TK2.tki, TK2.dept, TK2.loc FROM DB.dbo.timek as TK2 WITH(NOLOCK)) AS TK2
ON TK2.tki = M.tkid
WHERE DPT.Department = 'Casualty'
AND UPPER (C.ClientName) LIKE '%LIMITED%'
AND CL.date > '31/12/2014'
AND CL.Date IS NULL
AND TK.tkloc = 'loc1' OR TK.tkloc = 'loc2'
ORDER BY M.ref
My first answer would be because you're using INNER JOIN. This only returns matches between the 2 tables. TRY FULL OUTER JOIN which will return all values regardless of matches and will include NULLS.
If you were looking to return all rows regardless of matches including NULLS from only one of the tables then use RIGHT or LEFT JOIN.
Say i had 2 tables ('Person' and 'Figure'). Not every person may have entered a figure on any one day. But an example may be i want to return all people regardless of whether they entered a figure or not on a certain day.
My initial approach to this would be a LEFT join because i want to return of all the people(left table) regardless of there being any matches in the figure table(right table)
FROM Person P
LEFT JOIN Figure F
ON P.ID = F.ID
This would produce a result such as
Name Figure
Sam 20
Ben 30
Matt NULL
Simon NULL
Whereas,
An inner join would produce only matching values not including nulls
Name Figure
Sam 20
Ben 30
Left join works the same way as right join but in the opposite direction. This is most likely the problem you were facing. But i hope this helped
I think the problem is in the last part of the where condition.
You should use brackets.
`WHERE DPT.Department = 'Casualty'
AND UPPER (C.ClientName) LIKE '%LIMITED%'
AND CL.date > '31/12/2014'
AND CL.Date IS NULL
AND (TK.tkloc = 'loc1' OR TK.tkloc = 'loc2')`
or
`WHERE DPT.Department = 'Casualty'
AND UPPER (C.ClientName) LIKE '%LIMITED%'
AND CL.date > '31/12/2014'
AND CL.Date IS NULL
AND TK.tkloc IN ('loc1', 'loc2')`

How to increase performance of this query?

I have an SQL query, it is running on MSSQL 2008 R2
View vMobileLastMobileHistory has about 1000 rows and
select * from vMobileLastMobileHistory is taking 0.2 sec
but this query is taking 5 seconds, how can I optimize this code?
(I think the problem is INTERSECT but I dont know how change this)
SELECT DISTINCT *
FROM
(
SELECT vMobileLastMobileHistory.*
FROM vMobileLastMobileHistory
LEFT OUTER JOIN MobileType_DomainAction ON
MobileType_DomainAction.tiMobileType = vMobileLastMobileHistory.tiMobileType
LEFT OUTER JOIN MobileType_User ON
MobileType_User.MobileID = MobileType_DomainAction.ID
WHERE MobileType_User.UserID = #UserID OR #UserID = - 1
INTERSECT
SELECT vMobileLastMobileHistory.*
FROM vMobileLastMobileHistory
LEFT OUTER JOIN dbo.Region_User ON
dbo.vMobileLastMobileHistory.strRegion = dbo.Region_User.strRegion
WHERE Region_User.iSystemUser = #UserID OR #UserID = - 1
INTERSECT
SELECT vMobileLastMobileHistory.*
FROM vMobileLastMobileHistory
LEFT OUTER JOIN Contractor_User ON
vMobileLastMobileHistory.strContractor = Contractor_User.strContractor
WHERE Contractor_User.iSystemUser = #UserID OR #UserID = - 1
)
The problem is that if you have any indexes on your iSytemUser columns, the optimise is unable to use them because it has to account for a specific userID being passed, or returning all results, it would be better to logically separate your two cases. In addition, since you don't care about any columns in the auxiliary tables, you could use EXISTS in your case of specific users to take advantage of a semi join:
IF (#UserID = -1)
BEGIN
SELECT DISTINCT *
FROM vMobileLastMobileHistory;
END
ELSE
BEGIN
SELECT DISTINCT *
FROM vMobileLastMobileHistory AS mh
WHERE EXISTS
( SELECT 1
FROM Contractor_User AS cu
WHERE cu.strContractor = mh.strContractor
AND cu.iSystemUser = #UserID
)
AND EXISTS
( SELECT 1
FROM Region_User AS ru
WHERE ru.strRegion = mh.strRegion
AND ru.iSystemUser = #UserID
)
AND EXISTS
( SELECT 1
FROM MobileType_DomainAction AS da
INNER JOIN MobileType_User AS mu
ON mu.MobileID = da.ID
WHERE da.tiMobileType = mh.tiMobileType
AND mu.iSystemUser = #UserID
);
END
Now you can have two execution plans for each case (returning all results, or for a specific user), in each case you only need to read from vMobileLastMobileHistory once, and you also limit the sorts required by removing the INTERSECT and replacing with 3 EXISTS clauses.
If they don't already exist then you may also which to consider some indexes on your tables. A good way of finding out what indexes would help is to run the query in SQL Server Management Studio with the option "Show Actual Execution Plan" enabled, this will then show you any missing indexes.
Most of time Intersect and Inner Join will be same. You are not share your data, so based on my knowledge and this link, I just replace intersect query into Inner join query as :
--I think you don't need distinct upper query. If you have issue inform me.
SELECT DISTINCT vml.*
FROM vMobileLastMobileHistory vml
LEFT OUTER JOIN MobileType_DomainAction mtda ON mtda.tiMobileType = vml.tiMobileType
LEFT OUTER JOIN MobileType_User ON MobileType_User.MobileID = mtda.ID
LEFT OUTER JOIN dbo.Region_User ON dbo.vml.strRegion = dbo.Region_User.strRegion
LEFT OUTER JOIN Contractor_User ON vml.strContractor = Contractor_User.strContractor
WHERE
(MobileType_User.UserID = #UserID
and Region_User.iSystemUser = #UserID or Contractor_User.iSystemUser = #UserID
) OR #UserID = - 1

SQL Server left join with conditionals not giving me results I want

Query first, and then question:
SELECT DISTINCT p.postID, p.postGUID, p.postTitle, p.postTypeID, p.sequence, m.firstname, m.lastname, pt.postTypeName, mc.acceptRejectDate
FROM post p
INNER JOIN member m ON p.memberGUID = m.memberGUID
INNER JOIN postType pt ON p.postTypeID = pt.postTypeID
LEFT JOIN masterClass mc ON (p.postGUID = mc.postGUID AND mc.isMemberPrivate = 0 AND mc.status = 2 AND mc.acceptRejectDate IS NOT NULL)
WHERE p.postTitle LIKE '%five%'
AND p.isActive = 1
ORDER BY p.postTypeID, p.sequence, mc.acceptRejectDate
What I'm trying to do here is grab all results from the "posts" table that have isActive = 1 and the title includes "five." Easy enough.
Some of the results also have an association to the masterClass table. For those results I only want to include them if isMemberPrivate is zero, status is 2, and there is an acceptRejectDate.
I thought this would be easy enough, but when I run the query I get results including some posts that don't meet the master class join criteria. And, there are a few results that are displaying as having null values but clearly don't when I look at the raw data.
Is there anything in this query that looks wrong and would cause my results to be incorrect?
From reading your question, I believe you want to include all results that either have no record in the masterClass table, or do have records in the masterClass table that meet your criteria. If you move your criteria down to the WHERE section, and add a check for records that did not match anything in the masterClass table, you should get what you want.
SELECT DISTINCT p.postID, p.postGUID, p.postTitle, p.postTypeID, p.sequence, m.firstname, m.lastname, pt.postTypeName, mc.acceptRejectDate
FROM post p
INNER JOIN member m ON p.memberGUID = m.memberGUID
INNER JOIN postType pt ON p.postTypeID = pt.postTypeID
LEFT JOIN masterClass mc ON p.postGUID = mc.postGUID
WHERE p.postTitle LIKE '%five%'
AND p.isActive = 1
AND ( mc.postGUID is NULL OR
(mc.isMemberPrivate = 0 AND mc.status = 2 AND mc.acceptRejectDate IS NOT NULL)
)
ORDER BY p.postTypeID, p.sequence, mc.acceptRejectDate
Edit: Changed field looking for NULLs from mc.acceptRejectDate to mc.postGUID
Try this to clearly separate JOIN and filter conditions
...
LEFT JOIN
(
SELECT postGUID, acceptRejectDate
FROM masterClass
WHERE isMemberPrivate = 0 AND status = 2 AND acceptRejectDate IS NOT NULL
) mc ON p.postGUID = mc.postGUID
...

Resources