My query doesn't get the right value - sql

My query doesn't give the right output.
I tried with different way such as with CTE as well but nothing help.
I'm trying to write View to display available products with quantity but as some data is not available in StockCloseInventory table it displaying me 0 even though records are available for some products.
Here is my query:
SELECT TOP (100) PERCENT
SOI.InStockPrdID
, SOI.ProdName
, SOI.TradeDrugId
, ISNULL(SOI.Quantity, 0) AS INQTY
, ISNULL(SCI.QuantitySold, 0) AS OUTQTY
, ISNULL(SOI.Quantity, 0) - ISNULL(SCI.QuantitySold, 0) AS AQTY
, SOI.UnitPrice
FROM dbo.StockInventory AS SOI
LEFT OUTER JOIN dbo.StockCloseInventory AS SCI
ON SOI.InStockPrdID = SCI.InStockPrdID
AND SOI.SoldOut <> 1
LEFT OUTER JOIN dbo.vwTradeDrug AS VTD
ON SOI.TradeDrugId = VTD.TradeDrugId
ORDER BY SOI.ProdName
And this is the output of my View:
And here is my Tables
Table: StockInventory
Table: StockCloseInventory
vwTradeDrug view:
CREATE VIEW [dbo].[vwTradeDrug]
AS
SELECT
T.TradeDrugId
, T.GenericDrugId
, T.TradeName
, G.GenericProprietaryName
, G.DosageType
, G.Strength
, T.TradeName + ',' + G.GenericProprietaryName + ',' + G.DosageType
+ ',' + G.Strength AS TradeFullName
FROM dbo.TradeDrug AS T
INNER JOIN Inventory.GenericDrug AS G
ON T.GenericDrugId = G.GenericId
GO

The problem is with SOI.SoldOut <> 1
First, as Pouya Kamyar pointed out, it is more conventional to include this in a WHERE clause than in a JOIN.
However, the issue is that the value of this field is mostly NULL.
What you want is something more like this:
WHERE SOI.SoldOut IS NULL
OR SOI.SoldOut <> 1

I Suggest bring "AND SOI.SoldOut <> 1" in WHERE clause not in join terms

Either change the null data in sold out to be 0 or 1 OR...
handle the nulls so that the boolean compare results in a true or false instead of "NULL" as below.
SELECT TOP (100) PERCENT
SOI.InStockPrdID
, SOI.ProdName
, SOI.TradeDrugId
, ISNULL(SOI.Quantity, 0) AS INQTY
, ISNULL(SCI.QuantitySold, 0) AS OUTQTY
, ISNULL(SOI.Quantity, 0) - ISNULL(SCI.QuantitySold, 0) AS AQTY
, SOI.UnitPrice
FROM dbo.StockInventory AS SOI
LEFT OUTER JOIN dbo.StockCloseInventory AS SCI
ON SOI.InStockPrdID = SCI.InStockPrdID
AND ISNULL(SOI.SoldOut,0) <> 1 -- this should do the trick
LEFT OUTER JOIN dbo.vwTradeDrug AS VTD
ON SOI.TradeDrugId = VTD.TradeDrugId
ORDER BY SOI.ProdName
Remember Boolean compares on Null values result in NULL (a third value in a Boolean compare!) so SOI.SoldOut <> 1 when SOI.SoldOut is null will result in NULL instead of a true/false you're expecting.

Related

Conditionally adding a column in a SQL query

I'm trying to add a conditional SELECT column in a query and I'm having trouble writing it out:
SELECT v.GLTypeID,
v.something,
v.somethignElse,
CASE WHEN (SELECT TOP 1 Value FROM InterfaceParam WHERE InterfaceId = 0 AND Descr = 'gf') = 1 THEN a.CreditID ELSE NULL END AS CreditMemoID
FROM vGLDetails2 v
....
LEFT OUTER JOIN AssociationFund f
ON v.FundID = f.FundID
LEFT JOIN dbo.APLedger a ON v.RelID = a.APLedgerID AND v.RelTypeID IN (39, 40)
....
ORDER BY v.Code;
The query above works, however if the CASE statement is still returning an additional column regardless of the result of the subquery. How can I remove it if the subquery doesn't return a row?
How can I do this?
Change the location of AS. For example:
SELECT v.GLTypeID,
v.something,
v.somethignElse,
CASE WHEN (
SELECT TOP 1 Value
FROM InterfaceParam
WHERE InterfaceId = 0 AND Descr = 'creditMemo') = 1
THEN a.CreditID -- AS is not valid here
END AS CreditMemoID -- AS is valid here
FROM vGLDetails2 v
....
LEFT OUTER JOIN AssociationFund f
ON v.FundID = f.FundID
LEFT JOIN dbo.APLedger a ON v.RelID = a.APLedgerID AND v.RelTypeID IN (39, 40)
....
ORDER BY v.Code;
Note: I removed ELSE NULL since this is the default behavior of CASE.

How to use exists for a complete list of a product?

This is the query to find out if a particular car has W1, W2, WA, WH conditions. How can I modify my query so that I can get a list of all the cars as yes or no for these conditions?
NOTE: Here, I have put v.[carnumber] = 't8302' but I need a complete list.
SELECT
CASE
WHEN EXISTS (
SELECT co.[alias]
FROM [MTI_TAXI].[vehicle] v
LEFT JOIN [MTI_SYSTEM].[Conditions] co with (nolock) on v.DispatchSystemID = co.DispatchSystemID and (v.Conditions & co.conditionvalue > 0)
WHERE co.[alias] in ('W1', 'W2', 'WA', 'WH') and v.[DispatchSystemID] = 6 and v.[CarNumber] = 't8302')
THEN cast ('Yes' as varchar)
ELSE cast ('No' as varchar)
END AS [WATS]
OUTPUT - ( WATS - No )
But, here are all the cars but I am getting yes to WATS condition which is incorrect
enter image description here
Simply utilizing your provided filters and moving the EXISTS to be used in an OUTER APPLY statement:
SELECT
CASE
WHEN [find_wats].[Found] = 1
THEN 'Yes'
ELSE 'No'
END AS [WATS]
FROM
[MTI_TAXI].[vehicle] AS v
OUTER APPLY (SELECT TOP (1)
1 AS [Found]
FROM
[MTI_SYSTEM].[Conditions] AS co
WHERE
v.DispatchSystemID = co.DispatchSystemID
AND
(v.Conditions & co.conditionvalue > 0)
AND
co.[alias] IN ('W1', 'W2', 'WA', 'WH')
AND
v.[DispatchSystemID] = 6) AS [find_wats];
Using this set up, you can then use [find_wats].[Found] = 1 to determine that your record within the table [MTI_TAXI].[vehicle] found a match in [MTI_TAXI].[Conditions] (using your provided criteria) while still maintaining a single record in your final result set for each record originally in the table [MTI_TAXI].[vehicle].
Use count(*) to assert that there was exactly 1 row found by adding:
group by co.[alias]
having count(*) = 1
So the whole query becomes:
SELECT
CASE
WHEN EXISTS (
SELECT co.[alias]
FROM [MTI_TAXI].[vehicle] v
LEFT JOIN [MTI_SYSTEM].[Conditions] co with (nolock)
on v.DispatchSystemID = co.DispatchSystemID
and (v.Conditions & co.conditionvalue > 0)
WHERE co.[alias] in ('W1', 'W2', 'WA', 'WH')
and v.[DispatchSystemID] = 6
and v.[CarNumber] = 't8302'
group by co.[alias]
having count(*) = 1
) THEN cast ('Yes' as varchar)
ELSE cast ('No' as varchar)
end AS [WATS]

sqlite query not getting all records if 1 table has missing data

I've got a very complex database with a lot of tables in SQLite. I'm trying to design a query that will report out a lot of data from those tables and also report out those sheep who may not have a record in one or more tables.
My query is:
SELECT sheep_table.sheep_id,
(SELECT tag_number FROM id_info_table WHERE official_id = "1" AND id_info_table.sheep_id = sheep_table.sheep_id AND (tag_date_off IS NULL or tag_date_off = '')) AS fedtag,
(SELECT tag_number FROM id_info_table WHERE tag_type = "4" AND id_info_table.sheep_id = sheep_table.sheep_id AND (tag_date_off IS NULL or tag_date_off = '')) AS farmtag,
(SELECT tag_number FROM id_info_table WHERE tag_type = "2" AND id_info_table.sheep_id = sheep_table.sheep_id AND (tag_date_off IS NULL or tag_date_off = '') and ( id_info_table.official_id is NULL or id_info_table.official_id = 0 )) AS eidtag,
sheep_table.sheep_name, codon171_table.codon171_alleles, sheep_ebv_table.usa_maternal_index, sheep_ebv_table.self_replacing_carcass_index, cluster_table.cluster_name, sheep_evaluation_table.id_evaluationid,
(sheep_table.birth_type +
sheep_table.codon171 +
sheep_evaluation_table.trait_score01 +
sheep_evaluation_table.trait_score02 +
sheep_evaluation_table.trait_score03 +
sheep_evaluation_table.trait_score04 +
sheep_evaluation_table.trait_score05 +
sheep_evaluation_table.trait_score06 +
sheep_evaluation_table.trait_score07 +
sheep_evaluation_table.trait_score08 +
sheep_evaluation_table.trait_score09 +
sheep_evaluation_table.trait_score10 +
(sheep_evaluation_table.trait_score11 / 10 )) as overall_score, sheep_evaluation_table.sheep_rank, sheep_evaluation_table.number_sheep_ranked,
sheep_table.alert01,
sheep_table.birth_date, sheep_sex_table.sex_abbrev, birth_type_table.birth_type,
sire_table.sheep_name as sire_name, dam_table.sheep_name as dam_name
FROM sheep_table
join codon171_table on sheep_table.codon171 = codon171_table.id_codon171id
join sheep_cluster_table on sheep_table.sheep_id = sheep_cluster_table.sheep_id
join cluster_table on cluster_table.id_clusternameid = sheep_cluster_table.which_cluster
join birth_type_table on sheep_table.birth_type = birth_type_table.id_birthtypeid
join sheep_sex_table on sheep_table.sex = sheep_sex_table.sex_sheepid
join sheep_table as sire_table on sheep_table.sire_id = sire_table.sheep_id
join sheep_table as dam_table on sheep_table.dam_id = dam_table.sheep_id
left outer join sheep_ebv_table on sheep_table.sheep_id = sheep_ebv_table.sheep_id
left outer join sheep_evaluation_table on sheep_table.sheep_id = sheep_evaluation_table.sheep_id
WHERE (sheep_table.remove_date IS NULL or sheep_table.remove_date is '' )
and (eval_date > "2014-10-03%" and eval_date < "2014-11%")
and sheep_ebv_table.ebv_date = "2014-11-01"
order by sheep_sex_table.sex_abbrev asc, cluster_name asc, self_replacing_carcass_index desc, usa_maternal_index desc, overall_score desc
If a given sheep does not have a record in the evaluation table or does not have a record in the EBV table no record is returned. I need all the current animals returned with all available data on them and just leave the fields for EBVs and evaluations null if they have no data.
I'm not understanding why I'm not getting them all since none of the sheep have all 3 ID types (federal, farm and EID) so there are nulls in those fields and I was expecting nulls in the evaluation sum and ebv fields as well.
Totally lost in what to do to fix it.
The problem would appear to be that you're using eval_date in the WHERE statement. I'm assuming that eval_date is in the sheep_evaluation_table, so when you use it in WHERE, it gets rid of any rows where eval_date is NULL, which it would be when you're using a LEFT OUTER JOIN and there's no matching record in sheep_evaluation_table.
Try putting the eval_date filter on the join instead, like this:
left outer join sheep_evaluation_table on sheep_table.sheep_id = sheep_evaluation_table.sheep_id
AND (eval_date > "2014-10-03%" and eval_date < "2014-11%")
WHERE (sheep_table.remove_date IS NULL or sheep_table.remove_date is '' )

Return First 4 Rows, then Repeat for Grouping

I am trying to return data that will ultimately populate a label.
Each label is going onto a box, and the box can only have 4 items in it.
If a delivery has more than 4 items, then I need one label per 4.
Each row of data returned will populate one label, so if the delivery contains 9 items, then I need 3 rows of data returned.
Below is my current query, which is returning all items into a comma separated value using Stuff.
I want it so the first 4 rows for the delivery return in the first row, then the next 4 in the second and so on.
My Field LineOrd returns correctly if there are more than 4 lines on the dispatch.
select Distinct
delivery_header.dh_datetime,
delivery_header.dh_number,
order_header.oh_order_number as 'Order No',
order_header_detail.ohd_delivery_name,
order_header_detail.ohd_delivery_address1,
order_header_detail.ohd_delivery_address2,
order_header_detail.ohd_delivery_address3,
order_header_detail.ohd_delivery_town,
order_header_detail.ohd_delivery_county,
order_header_detail.ohd_delivery_postcode,
order_header_detail.ohd_delivery_country,
STUFF((Select ', '+convert(varchar(50),convert(decimal(8,0),DL.dli_qty))+'x '+OLI.oli_description
from delivery_header DH join delivery_line_item DL on DL.dli_dh_id = DH.dh_id join order_line_item OLI on OLI.oli_id = DL.dli_oli_id
Outer APPLY
(select
case when DelCurLine.CurLine <= 4
then '1'
Else
Case when DelCurLine.CurLine <= 8
then '2'
Else '3'
End
End +'-'+order_header.oh_order_number as LineOrd) as StuffLineOrder
Where DH.dh_id = delivery_header.dh_id And StuffLineOrder.LineOrd = LineOrder.LineOrd
FOR XML PATH('')),1,1,'') as Items,
LineOrder.LineOrd
from delivery_header
join delivery_line_item on delivery_line_item.dli_dh_id = delivery_header.dh_id
join order_line_item on order_line_item.oli_id = delivery_line_item.dli_oli_id
join order_header on order_header.oh_id = order_line_item.oli_oh_id
join order_header_detail on order_header_detail.ohd_oh_id = order_header.oh_id
join variant_detail on variant_detail.vad_id = order_line_item.oli_vad_id
join stock_location on stock_location.sl_id = order_line_item.oli_sl_id
Outer APPLY
(select count(DLI.dli_id) CurLine from delivery_line_item DLI where DLI.dli_dh_id = delivery_header.dh_id and DLI.dli_id <= delivery_line_item.dli_id)
as DelCurLine
Outer APPLY
(select
case when DelCurLine.CurLine <= 4
then '1'
Else
Case when DelCurLine.CurLine <= 8
then '2'
Else '3'
End
End +'-'+order_header.oh_order_number as LineOrd) as LineOrder
Outer APPLY
(select convert(varchar(50),convert(decimal(8,0),delivery_line_item.dli_qty))+'x '+order_line_item.oli_description as LineName) as LineName
where
delivery_header.dh_datetime between #DateFrom and #DateTo
and stock_location.sl_id = #StockLoc
and (order_header.oh_order_number = #OrderNo or #AllOrder = 1)
order by
delivery_header.dh_datetime,
delivery_header.dh_number,
order_header.oh_order_number,
order_header_detail.ohd_delivery_name,
order_header_detail.ohd_delivery_address1,
order_header_detail.ohd_delivery_address2,
order_header_detail.ohd_delivery_address3,
order_header_detail.ohd_delivery_town,
order_header_detail.ohd_delivery_county,
order_header_detail.ohd_delivery_postcode,
order_header_detail.ohd_delivery_country
You can use ROW_NUMBER() with a division by 4. This truncate the decimal because numerator is an interger. This give you group number with a maximum of four row in each group. You can then adjust your query to use this group number in a "group by" clause to return grouped rows into a single one.
Exemple here :
SELECT RawData.BoxGroup,
MIN(dh_datetime),
MIN(dh_number),
MIN(order_header.oh_order_number) as 'Order No'
--And so on
FROM
(SELECT BoxGroup = (ROW_NUMBER() OVER(ORDER BY (SELECT 1)) - 1) / 4,
*
FROM [TableNameOrQuery]) AS RawData
GROUP BY RawData.BoxGroup
Hope this help.

TOP Returning null

I have the following view below. The second nested select is always returning null when I use the TOP(1) clause, but when I remove this clause it returns the data as expected, just more rows than is needed. Does anyone see anything that would explain this?
SELECT TOP (100) PERCENT
a.ITEMID AS Model
,id.CONFIGID
,id.INVENTSITEID AS SiteId
,id.INVENTSERIALID AS Serial
,it.ITEMNAME AS Description
,CASE WHEN it.DIMGROUPID LIKE '%LR-Y' THEN 'Y'
ELSE 'N'
END AS SerialNumberReqd
,ISNULL(it.PRIMARYVENDORID, N'') AS Vendor
,ISNULL(vt.NAME, N'') AS VendorName
,id.INVENTLOCATIONID AS Warehouse
,id.WMSLOCATIONID AS Bin
,ISNULL(CONVERT(varchar(12), CASE WHEN C.DatePhysical < '1901-01-01'
THEN NULL
ELSE C.DatePhysical
END, 101), N' ') AS DeliveryDate
,CASE WHEN (a.RESERVPHYSICAL > 0
OR C.StatusIssue = 1)
AND c.TransType = 0 THEN C.PONumber
ELSE ''
END AS SoNumber
,'' AS SoDetail
,ISNULL(C.PONumber, N'') AS RefNumber
,ISNULL(CONVERT(varchar(12), CASE WHEN ins.ProdDate < '1901-01-01'
THEN NULL
ELSE ins.PRODDATE
END, 101), N' ') AS DateReceived
,it.STKSTORISGROUPID AS ProdGroup
,ISNULL(CONVERT(varchar(12), CASE WHEN ins.ProdDate < '1901-01-01'
THEN NULL
ELSE ins.PRODDATE
END, 101), N' ') AS ProductionDate
,it.ITEMGROUPID
,it.STKSTORISGROUPID AS MerchandisingGroup
,CASE WHEN a.postedValue = 0
THEN (CASE WHEN D.CostAmtPosted = 0 THEN D.CostAmtPhysical
ELSE D.CostAmtPosted
END)
ELSE a.POSTEDVALUE
END AS Cost
,CASE WHEN a.PHYSICALINVENT = 0 THEN a.Picked
ELSE a.PhysicalInvent
END AS PhysicalOnHand
,ins.STKRUGSQFT AS RugSqFt
,ins.STKRUGVENDSERIAL AS RugVendSerial
,ins.STKRUGVENDDESIGN AS RugVendDesign
,ins.STKRUGEXACTSIZE AS RugExactSize
,ins.STKRUGCOUNTRYOFORIGIN AS RugCountryOfOrigin
,ins.STKRUGQUALITYID AS RugQualityId
,ins.STKRUGCOLORID AS RugColorId
,ins.STKRUGDESIGNID AS RugDesignId
,ins.STKRUGSHAPEID AS RugShapeId
,CASE WHEN (a.AVAILPHYSICAL > 0) THEN 'Available'
WHEN (id.WMSLOCATIONID = 'NIL') THEN 'Nil'
WHEN (a.RESERVPHYSICAL > 0)
AND (c.TransType = 0) THEN 'Committed'
WHEN (a.RESERVPHYSICAL > 0) THEN 'Reserved'
WHEN (id.WMSLOCATIONID LIKE '%-Q') THEN 'Damaged'
WHEN (a.Picked > 0) THEN 'Picked'
ELSE 'UNKNOWN'
END AS Status
,'' AS ReasonCode
,'' AS BaseModel
,ISNULL(CAST(ins.STKSTORISCONFIGINFO AS nvarchar(1000)), N'') AS StorisConfigInfo
,ISNULL(C.ConfigSummary, N'') AS ConfigSummary
FROM
dbo.INVENTSUM AS a WITH (NOLOCK)
INNER JOIN dbo.INVENTDIM AS id WITH (NOLOCK)
ON id.DATAAREAID = a.DATAAREAID
AND id.INVENTDIMID = a.INVENTDIMID
LEFT OUTER JOIN dbo.INVENTTABLE AS it WITH (NOLOCK)
ON it.DATAAREAID = a.DATAAREAID
AND it.ITEMID = a.ITEMID
LEFT OUTER JOIN dbo.VENDTABLE AS vt WITH (NOLOCK)
ON vt.DATAAREAID = it.DATAAREAID
AND vt.ACCOUNTNUM = it.PRIMARYVENDORID
LEFT OUTER JOIN dbo.INVENTSERIAL AS ins WITH (NOLOCK)
ON ins.DATAAREAID = id.DATAAREAID
AND ins.INVENTSERIALID = id.INVENTSERIALID
LEFT OUTER JOIN (SELECT TOP (1)
itt.ITEMID
,invt.INVENTSERIALID
,itt.DATEPHYSICAL AS DatePhysical
,itt.TRANSREFID AS PONumber
,itt.TRANSTYPE AS TransType
,itt.STATUSISSUE AS StatusIssue
,dbo.stkRowsToColumn(itt.INVENTTRANSID, 'STI') AS ConfigSummary
,itt.RECID
FROM
dbo.INVENTTRANS AS itt WITH (NOLOCK)
INNER JOIN dbo.INVENTDIM AS invt WITH (NOLOCK)
ON invt.DATAAREAID = itt.DATAAREAID
AND invt.INVENTDIMID = itt.INVENTDIMID
WHERE
(itt.DATAAREAID = 'STI')
AND (itt.TRANSTYPE IN (0, 2, 3, 8))
AND (invt.INVENTSERIALID <> '')
ORDER BY
itt.RECID DESC) AS C
ON C.ITEMID = a.ITEMID
AND C.INVENTSERIALID = id.INVENTSERIALID
LEFT OUTER JOIN (SELECT TOP (1)
itt2.ITEMID
,invt2.INVENTSERIALID
,itt2.COSTAMOUNTPOSTED AS CostAmtPosted
,itt2.COSTAMOUNTPHYSICAL + itt2.COSTAMOUNTADJUSTMENT AS CostAmtPhysical
,itt2.RECID
FROM
dbo.INVENTTRANS AS itt2 WITH (NOLOCK)
INNER JOIN dbo.INVENTDIM AS invt2 WITH (NOLOCK)
ON invt2.DATAAREAID = itt2.DATAAREAID
AND invt2.INVENTDIMID = itt2.INVENTDIMID
WHERE
(itt2.DATAAREAID = 'STI')
AND (itt2.TRANSTYPE IN (0, 2, 3, 4, 6, 8))
AND (invt2.INVENTSERIALID <> '')
ORDER BY
itt2.RECID DESC) AS D
ON D.ITEMID = a.ITEMID
AND D.INVENTSERIALID = id.INVENTSERIALID
WHERE
(a.DATAAREAID = 'STI')
AND (a.CLOSED = 0)
AND (a.PHYSICALINVENT > 0)
AND (it.ITEMGROUPID LIKE 'FG-%'
OR it.ITEMGROUPID = 'MULTISHIP')
ORDER BY
SiteId
,Warehouse
Presumably, the top value in the subquery doesn't meet the subsequent join conditions. That is, this condition is not met:
D.ITEMID = a.ITEMID AND D.INVENTSERIALID = id.INVENTSERIALID
You are using a left outer join, so NULL values are filled in.
EDIT:
To re-iterate. When you run it with top 1, there are no values (for at least some combinations of the two variables). So, NULL will be filled in for these values. After all, top 1 (with or without the parentheses) returns only one row.
When you run it returning multiple rows, presumably there are matches. For the rows that match, the corresponding values are put it. This is the way that left outer join works.
Gordon's answer is correct as to why I was getting a few rows when removing top and none when I had it. The subquery in question was returning all the rows in the InventTrans table (5 million+) so when I used top, it was just getting the first row which didn't have anything. I realized this was the case when I was trying random high values (e.g 50000) in the TOP clause.
The ultimate fix was to change the left outer joins on the C and D subqueries to Cross Apply, and then change the where clauses to better filter the table (e.g itt.itemid = a.itemid and invt1.inventserialid = id.inventserialid). Using that, I was able to use TOP 1 as expected.