Blog coding and discussion of coding about JavaScript, PHP, CGI, general web building etc.

Monday, March 28, 2016

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

followed by right join is effectively an inner join in some cases

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:

  1. 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.

  2. 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

Related Posts:

0 comments:

Post a Comment

Popular Posts

Fun Page

Powered by Blogger.