SQL Server Query - Weird Behaviour
SQL Server Query - Weird Behaviour
Consider below SQL.
SELECT DISTINCT bvc_Order.ID, bvc_OrderItem.ProductID, bvc_OrderItem_BundleItem.ProductID FROM dbo.bvc_OrderItem WITH (nolock) RIGHT OUTER JOIN dbo.bvc_Order WITH (nolock) LEFT OUTER JOIN dbo.bvc_User WITH (nolock) ON dbo.bvc_Order.UserID = dbo.bvc_User.ID LEFT OUTER JOIN dbo.Amazon_Merchants WITH (nolock) ON dbo.bvc_Order.CompanyID = dbo.Amazon_Merchants.ID ON dbo.bvc_OrderItem.OrderID = dbo.bvc_Order.ID LEFT OUTER JOIN dbo.bvc_OrderItem_BundleItem WITH (nolock) ON dbo.bvc_OrderItem.ID = dbo.bvc_OrderItem_BundleItem.OrderItemID LEFT OUTER JOIN dbo.bvc_Product WITH (nolock) ON dbo.bvc_OrderItem.ProductID = dbo.bvc_Product.ID WHERE 1=1 AND (bvc_Order.StatusCode <> 1 AND bvc_Order.StatusCode <> 999) AND ( bvc_OrderItem.ProductID IN ('28046_00') OR bvc_OrderItem_BundleItem.ProductID IN ('28046_00')) AND bvc_Order.OrderSource = 56;
The query when I execute against my database, it returns 85 rows. Well, that is not correct. If I just remove the part "AND bvc_Order.OrderSource = 56" it returns back 5 rows which is really correct. Strange..... Another thing, if I remove the part
OR bvc_OrderItem_BundleItem.ProductID IN ('28046_00')
it will also return the 5 rows as expected even with bvc_Order.OrderSource filter. I am not sure why it is adding more rows while I am trying to reduce rows by using filters.
the table bvc_OrderItem_BundleItem doesn't contain any rows for the result order ids or OrderItemIDs
[edit]
Thanks guys, I tried to remove the LEFT/RIGHT Join Mix but Query manager doesn't allows only LEFT, it does add at least one RIGHT join. I updated the SQL to remove extra tables and now we have only three. But same result
SELECT DISTINCT dbo.bvc_Order.ID, dbo.bvc_OrderItem.ProductID, dbo.bvc_OrderItem_BundleItem.ProductID AS Expr1 FROM dbo.bvc_OrderItem LEFT OUTER JOIN dbo.bvc_OrderItem_BundleItem ON dbo.bvc_OrderItem.ID = dbo.bvc_OrderItem_BundleItem.OrderItemId RIGHT OUTER JOIN dbo.bvc_Order ON dbo.bvc_OrderItem.OrderID = dbo.bvc_Order.ID WHERE 1=1 AND (bvc_Order.StatusCode <> 1 AND bvc_Order.StatusCode <> 999) AND ( bvc_OrderItem.ProductID IN ('28046_00') OR bvc_OrderItem_BundleItem.ProductID IN ('28046_00') ) AND bvc_Order.OrderSource = 56;
Answer by usr for SQL Server Query - Weird Behaviour
When you say
WHERE bvc_Order.OrderSource = 56
that evaluates to false when bvc_Order.OrderSource
is NULL
. If the LEFT/RIGHT
join failed then it will be NULL
. This effectively turns the LEFT/RIGHT
join into an inner join.
You probably should write the predicate into the ON
clause. An alternative approach, which might not deliver the same results, is:
WHERE (bvc_Order.OrderSource IS NULL OR bvc_Order.OrderSource = 56)
The other predicates have the same problem:
Another thing, if I remove the part
OR bvc_OrderItem_BundleItem.ProductID IN ('28046_00')
it will also return the 5 rows as expected
When the join fails bvc_OrderItem_BundleItem.ProductID
is NULL
.
I also would recommend writing queries manually. If I understand you right this query comes from a designer. It's structure is quite confusing. I'm pulling up the most important comment:
Mixing left and right outer joins in a query is just confusing. You should start by rewriting the from clause to only use one type (and I strongly recommend left outer join). ? Gordon Linoff
Answer by siva krishna for SQL Server Query - Weird Behaviour
when you use LEFT outer join it will give all the rows from left table (dbo.bvc_OrderItem) once the your and, or conditions satisfies, the same thing happens with Right outer join too,
Those conditions (Left join, right join ) may not restrict the rows since rows from one table can be all, another table with some rows only.
check with your join condition
Then check you condition :
(bvc_Order.StatusCode <> 1 AND bvc_Order.StatusCode <> 999) if any rows satisfying this condition next check with another condition [bvc_OrderItem.ProductID IN ('28046_00') OR bvc_OrderItem_BundleItem.ProductID IN ('28046_00')]
Then bvc_Order.OrderSource = 56
compare the result of three queries and check the data in with the conditions and then write your complete query, so that you will understand where the mistake you have done.
Answer by Adam Silenko for SQL Server Query - Weird Behaviour
Like @usr writ, the reason of this unexpected (for you) result is, you build query with outer joins, and filter rows after join. If you need filter rows of outer joined tables, you should do this before join.
but probably you try build this:
SELECT DISTINCT o.ID, oi.ProductID, bi.ProductID AS Expr1 FROM dbo.bvc_Order as o LEFT JOIN dbo.bvc_OrderItem as oi on oi.OrderID = o.ID LEFT JOIN dbo.bvc_OrderItem_BundleItem as bi ON oi.ID = bi.OrderItemId WHERE 1=1 AND o.OrderSource = 56; AND o.StatusCode not in (1, 999) AND '28046_00' in (bi.ProductID, oi.ProductID )
Is this query give results what you need? if not, try change last condition, for example:
and (bi.ProductID = '28046_00' or bi.ProductID is null and oi.ProductID = '28046_00')
you can also put additional condition in to join conditions, for example:
SELECT DISTINCT o.ID, oi.ProductID, bi.ProductID AS Expr1 FROM dbo.bvc_Order as o LEFT JOIN dbo.bvc_OrderItem as oi on oi.OrderID = o.ID LEFT JOIN dbo.bvc_OrderItem_BundleItem as bi ON oi.ID = bi.OrderItemId and bi.ProductID in ('28046_00') --this join BundleItem only if ... WHERE 1=1 AND o.OrderSource = 56; AND o.StatusCode not in (1, 999) AND (oi.ProductID in ('28046_00') or bi.ProductID is nut null)
ah, and if you always need join bvc_Order with bvc_OrderItem then use inner join
Answer by TheGameiswar for SQL Server Query - Weird Behaviour
Few points to remember
1.And is applied during Virtual join phases
2.Where clause is applied after the final result
3.Left join
Lets break your query step by step..
dbo.bvc_OrderItem a1 LEFT OUTER JOIN dbo.bvc_OrderItem_BundleItem b1
Above output will be a single virtual table (logically) which contains all rows from b1 with matching rows from a1
now below predicates from your and clause will be applied
bvc_OrderItem.ProductID IN ('28046_00') OR bvc_OrderItem_BundleItem.ProductID IN ('28046_00')
which effectively eliminates all rows from bvc_OrderItem_BundleItem even if they have matches and gives result some thing like below if bvc_OrderItem_BundleItem.ProductID IN ('28046_00') is true
bvc_OrderItem bvc_OrderItem_BundleItem 28046 28046 null 1 null 2 null 3
if this condition(bvc_OrderItem.ProductID IN ('28046_00')) is true,then you are asking sql to ignore all rows in bvc_OrderItem ,which effectively means the same result set as above
bvc_OrderItem bvc_OrderItem_BundleItem other columns 28046 28046 null 1 null 2 null 3
next you are doing right outer join with dbo.bvc_Order which may qualifies for the join point I mentioned above
Assume ,you got below result set as output which preserves all of bvc_order table(rough output only for understanding due to lack of actual data)
bvc_OrderItem bvc_OrderItem_BundleItem statuscode ordersource 28046 28046 999 56 null 1 1 57 null 2 100 58 null 3 11 59
Next below AND predicates will be applied
status code <>1 and statuscode<> 999
which means ignore rows which match with bvc_order and has status of 1 ,999 even if they found matching rows
Next you are asking bvc_Order.OrderSource = 56; which means I don't care about other rows,preserve matching rows only for 56 and keep the rest as null
Hope this clarifies on what is happening step by step.A more better way can be provide some test data and show the expected output.
you also can control physical order of joins,you can try below to see if this is what you are trying to do..
SELECT DISTINCT dbo.bvc_Order.ID, dbo.bvc_OrderItem.ProductID, dbo.bvc_OrderItem_BundleItem.ProductID AS Expr1 dbo.bvc_OrderItem LEFT OUTER JOIN ( dbo.bvc_OrderItem_BundleItem RIGHT OUTER JOIN dbo.bvc_Order ON dbo.bvc_OrderItem.OrderID = dbo.bvc_OrderItem_BundleItem.OrderItemId )c on dbo.bvc_OrderItem.ID = c.bvc_OrderItem_BundleItem.OrderItemId WHERE 1=1 AND (bvc_Order.StatusCode <> 1 AND bvc_Order.StatusCode <> 999) AND ( bvc_OrderItem.ProductID IN ('28046_00') OR bvc_OrderItem_BundleItem.ProductID IN ('28046_00') ) AND bvc_Order.OrderSource = 56;
Answer by Richard_D for SQL Server Query - Weird Behaviour
It looks like you are using the Query Designer. I would avoid using this as this can make your queries extremely confusing. Your queries will be much more concise if you are designing them by hand. If you don't completely understand how inner/outer joins work, a great textbook that I used to teach myself SQL is Murach's SQL Server for Developers.
https://www.murach.com/shop/murach-s-sql-server-2012-for-developers-detail
Now, onto the answer.
I've been thinking about how to resolve your problem, and if you are trying to reduce the result set to 5 rows, why are you using multiple outer joins in the first place? I would consider switching the joins to inner joins instead of outer joins if you are looking for a very specific result set. I can't really provide you with a really comprehensive answer without looking at exactly what results you are trying to achieve, but here's a general idea based on what you've provided to all of us:
SELECT DISTINCT dbo.bvc_Order.ID, dbo.bvc_OrderItem.ProductID, dbo.bvc_OrderItem_BundleItem.ProductID AS 'bvc_OrderItem_BundleItem_ProductID' FROM dbo.bvc_OrderItem INNER JOIN dbo.bvc_OrderItem_BundleItem ON dbo.bvc_OrderItem.ID = dbo.bvc_OrderItem_BundleItem.OrderItemId INNER JOIN dbo.bvc_Order ON dbo.bvc_OrderItem.OrderID = dbo.bvc_Order.ID
Start here and then based upon what you are searching for, add where clauses to filter criteria.
Also, your where clause must be rewritten if you use an inner join instead of an outer join:
WHERE 1=1 --not really sure why this is here. This will always be true. Omit this statement to avoid a bad result set. AND (bvc_Order.StatusCode <> 1 AND bvc_Order.StatusCode <> 999) --this is saying, if the StatusCode is not equal to 1 and not equal to 999, don't include it. --Revised: Look for Status codes with 1 or 999 --bvc_Order.StatusCode = 1 OR bvc_Order.StatusCode = 999 AND (bvc_OrderItem.ProductID IN ('28046_00') --I would eliminate this unless you are looking to see if this exists in Product ID. You could also accomplish this if you are trying to see if this value is in both tables, change this to: bvc_OrderItem.ProductID = '28046_00' AND bvc_OrderItem_BundleItem.ProductID = '28046_00') --if you are trying to see if the order source is 56, use this. AND bvc_Order.OrderSource = 56;
If you are trying to find out rows that are not included in this result set, then I would use OUTER JOIN as necessary (LEFT preferred). Without more information about what you're looking for in your database, that's the best all of us can do.
Answer by John Rees for SQL Server Query - Weird Behaviour
When you have eliminated the impossible, whatever remains, however improbable, must be the truth? S.H.
It is impossible that an extra AND condition appended to a WHERE clause can ever result in extra rows. That would imply a database engine defect, which I hope I can assume is "impossible". (If not, then I guess it's back to square one).
That fact makes it easier to concentrate on possible reasons:
When you comment out
AND bvc_Order.OrderSource = 56;
then you also comment out the semicolon terminator. Is it possible that there is text following this query that is affecting it? Try putting a semicolon at the end of the previous line to make sure.
Depending on the tool you are using to run queries, sometimes when a query fails to execute, the tool mistakenly shows an old result set. Make sure your query is executing correctly by adding a dummy column to the SELECT statement to absolutely prove you are seeing live results. Which tool are you using?
Fatal error: Call to a member function getElementsByTagName() on a non-object in D:\XAMPP INSTALLASTION\xampp\htdocs\endunpratama9i\www-stackoverflow-info-proses.php on line 72
0 comments:
Post a Comment