Filtering by ALL or by selected attribute - sql-server

Excuse my language or SQL/Reporting wording
I am generating a report using Reporting Services where I have 2 drop down lists in order to show only by period and by category. I want it so if I select and I am shown all entries and if selected from any of the 2 drop downs then filter by those selections. I have a stored procedure which states the following on it's WHERE clause:
WHERE (dbo.PERIOD_LIST.PERIOD_DESC = #period) OR (dbo.CATEGORY.CATEGORY_DESC = #category)
However I cannot get this to work on Reporting Services/Visual Studio. I am shown ALL the entries instead of the filtered ones. I initialize #period and #category as NULL. So how can I make it so the report shows all rows when both attributes are null and still be able to filter by each or both of them?
How can I achieve this?
Edit: I used the suggestion Filip Popović gave me. If you're having this problem modify your prepared statement and make sure you refresh fields on your data sets since there's additional clauses on your prepared statement!

Note that in SQL, NULL = NULL is never evaluated to true. You can take it as NULL is not nothing it is something but unspecified. And You can't compare two unspecified values. You have to test COL1 IS NULL to get true if column has null value.
Try this:
WHERE ((dbo.PERIOD_LIST.PERIOD_DESC = #period) OR (#period IS NULL))
AND ((dbo.CATEGORY.CATEGORY_DESC = #category) OR (#category IS NULL))

Check the parameter for NULL as part of the condition:
WHERE ((#period IS NULL) OR (dbo.PERIOD_LIST.PERIOD_DESC = #period))
AND ((#category IS NULL) OR (dbo.CATEGORY.CATEGORY_DESC = #category))

Related

SQL Server - add to this query to first check for existence of a string

I have an nvarchar field in my database called CatCustom which contains comma-separated 5-character codes. It can contain as little as one code, or as many as 20 codes, separated by commas.
Right now, I use this query to add a new 5-character code to the field in given records (in this case the new code is LRR01):
UPDATE dbo.Sources
SET CatCustom = CONCAT_WS(', ', RTRIM(CatCustom), 'LRR01')
WHERE SourceID IN (1,2,3,4,5,8,9,44,63,45,101,102,222,344)
I need to add to this though: I need the record to be updated only if that 5-character code doesn't already exist somewhere in the CatCustom field, to ensure that code is not in there more than once.
How would I accomplish this?
EDIT: I really don't understand how this can be considered a duplicate of the suggested thread. This is a VERY specific case and has nothing to do with creating stored procedures and or variables. The alleged duplicated thread does not really help me - sorry.
Use STRING_SPLIT function to split the comma separated list and then add Not Exist condition in the WHERE clause like below
UPDATE dbo.Sources
SET CatCustom = CONCAT_WS(', ', RTRIM(CatCustom), 'LRR01')
WHERE SourceID IN (1,2,3,4,5,8,9,44,63,45,101,102,222,344)
AND NOT EXISTS (SELECT 1 FROM STRING_SPLIT(CatCustom, ',') where value = 'LRR01')
UPDATE dbo.Sources
SET
CatCustom = CONCAT_WS(', ', RTRIM(CatCustom), 'LRR01')
WHERE
SourceID IN (1,2,3,4,5,8,9,44,63,45,101,102,222,344)
AND CatCustom NOT LIKE '%LRR01%';

NULL values in multivalue parameter

This stored procedure beneath fills up my Projectphase parameter. So as you can see, the user first has to select #PurchaseOrder, which will then fill up the Projectphase Parameter.
CREATE PROCEDURE [dbo].[USP_GetProjectPhase]
#PurchaseOrder INT
AS
SELECT
pp.ProjectPhaseID, pp.Phase
FROM
ProjectPhase pp
WHERE
#PurchaseOrder = pp.PurchaseOrderId
Now when the user selects a PurchaseOrder that indeed has Projectphases, everything goes well. The issue situates itself in the purchaseorders that don't have a ProjectPhase.
The query that is used for my dataset shown on the report has the following line in the WHERE clause. It's a multivalue parameter cause the user needs to be able to select multiple Projectphases.
WHERE
reg.ProjectPhaseId IN (SELECT Value
FROM fnLocal_CmnParseList(#Phase,','))
I've tried UNIONS with NULL, NULL. I've tried stuff with ISNULL but I can't seem to be getting the query to execute when #ProjectPhase is NULL.
Some help would be greatly appreciated cause I've been cracking my head on this for too long now. Thanks
Finally found the answer:
[dbo].[USP_GetProjectPhase] #PurchaseOrder INT
AS
SELECT -1 AS 'ProjectPhaseID'
,'No Filter' AS 'Phase'
UNION
SELECT pp.ProjectPhaseID
,pp.Phase
FROM ProjectPhase pp
WHERE #PurchaseOrder = pp.PurchaseOrderId
In my query I changed the WHERE clause to:
WHERE (reg.ProjectPhaseId IN (SELECT Value FROM fnLocal_CmnParseList(#Phase,',')) OR #Phase = '-1')

SQL Server : While loop with nested If

I'm creating a stock market database and am stumped that the following works correctly EXCEPT for the last select that returns results (after which the select does not change on subsequent loops). I've tried to simplify the code as follows, thanks in advance for feedback (I'm still noob):
Three tables:
BuyOrders
SellOrders
MatchedOrders
Stored procedure to process a NewBuyOrder:
Insert NewBuyOrder to BuyOrders;
While (NewBuyOrder.SharesRemaining > 0 )
SELECT TOP 1
FROM SellOrders
WHERE SellOrders.Price <= NewBuyOrder.Price
ORDER BY SellOrders.Price, SellOrders.TimePlaced;
IF NewBuyOrder.SharesRemaining < SellOrders.SharesAvailable
UPDATE SellOrders.SharesAvailable = [difference];
UPDATE BuyOrders = 0;
INSERT INTO MatchedOrders;
SET NewBuyOrder.SharesRemaining = 0;
BREAK;
ELSE
UPDATE SellOrders = 0;
UPDATE BuyOrders = [difference];
INSERT INTO MatchedOrders;
SET NewBuyOrder.SharesRemaining = [difference];
CONTINUE;
In hope it might help someone else, I found the issue . . . I'm using local variables to store the matched SellOrderID. As such if the Select returns no match on a second pass through then the local variables were not getting updated (and hence erroneously reused in subsequent while loops until the If kicked in).
So I put a SET SellOrders.ID = 0 into the WHILE loop before the Select then below the Select added a IF SellOrders.ID = 0 and inside that a SET NewBuyOrder.SharesRemaining = 0 and BREAK (then made the first IF above into an ELSE IF).
I need to revisit the process to see if I can make it more elegant but would sincerely welcome thoughts on better ways to accomplish a process for matching the best available counteroffers in sequence. I've read but don't know much about cursors, plus think it transactionally superior not to SELECT a prioritized table of all matches rather than using my iterative loop -- but also have read suggestions not to use loops in SQL. Comments?
In addition I note the following: By itself a Select with no results returns a null set. Thus my original plan was to Select into my SP local variables and then use an IF EXISTS. I assume the local variable exists upon instantiation (even with no value) but am surprised that after a Select into the local variable with no results also did not fail an IF NULL test (i.e. presumably NULL cannot be inserted into a variable). What then is the value of an instantiated local variable with no value -- Blank?

NVarchar Prefix causes wrong index to be selected

I have an entity framework query that has this at the heart of it:
SELECT 1 AS dummy
FROM [dbo].[WidgetOrder] AS widgets
WHERE widgets.[SomeOtherOrderId] = N'SOME VALUE HERE'
The execution plan for this chooses an index that is a composite of three columns. This takes 10 to 12 seconds.
However, there is an index that is just [SomeOtherOrderId] with a few other columns in the "include". That is the index that should be used. And when I run the following queries it is used:
SELECT 1 AS dummy
FROM [dbo].[WidgetOrder] AS widgets
WHERE widgets.[SomeOtherOrderId] = CAST(N'SOME VALUE HERE' AS VARCHAR(200))
SELECT 1 AS dummy
FROM [dbo].[WidgetOrder] AS widgets
WHERE widgets.[SomeOtherOrderId] = 'SOME VALUE HERE'
This returns instantly. And it uses the index that is just SomeOtherOrderId
So, my problem is that I can't really change how Entity Framework makes the query.
Is there something I can do from an indexing point of view that could cause the correct index to be selected?
As far as I know, since version 4.0, EF doesn't generate unicode parameters for non-unicode columns. But you can always force non-unicode parameters by DbFunctions.AsNonUnicode (prior to EF6, DbFunctions is EntityFunctions):
from o in db.WidgetOrder
where o.SomeOtherOrderId == DbFunctions.AsNonUnicode(param)
select o
Try something like ....
SELECT 1 AS dummy
FROM [dbo].[WidgetOrder] AS widgets WITH (INDEX(Target_Index_Name))
WHERE widgets.[SomeOtherOrderId] = N'SOME VALUE HERE'
This query hint sql server explicitly what index to use to get resutls.

How do I update an XML column in sql server by checking for the value of two nodes including one which needs to do a contains (like) comparison

I have an xml column called OrderXML in an Orders table...
there is an XML XPath like this in the table...
/Order/InternalInformation/InternalOrderBreakout/InternalOrderHeader/InternalOrderDetails/InternalOrderDetail
There InternalOrderDetails contains many InternalOrderDetail nodes like this...
<InternalOrderDetails>
<InternalOrderDetail>
<Item_Number>FBL11REFBK</Item_Number>
<CountOfNumber>10</CountOfNumber>
<PriceLevel>FREE</PriceLevel>
</InternalOrderDetail>
<InternalOrderDetail>
<Item_Number>FCL13COTRGUID</Item_Number>
<CountOfNumber>2</CountOfNumber>
<PriceLevel>NONFREE</PriceLevel>
</InternalOrderDetail>
</InternalOrderDetails>
My end goal is to modify the XML in the OrderXML column IF the Item_Number of the node contains COTRGUID (like '%COTRGUID') AND the PriceLevel=NONFREE. If that condition is met I want to change the PriceLevel column to equal FREE.
I am having trouble with both creating the xpath expression that finds the correct nodes (using OrderXML.value or OrderXML.exist functions) and updating the XML using the OrderXML.modify function).
I have tried the following for the where clause:
WHERE OrderXML.value('(/Order/InternalInformation/InternalOrderBreakout/InternalOrderHeader/InternalOrderDetails/InternalOrderDetail/Item_Number/node())[1]','nvarchar(64)') like '%13COTRGUID'
That does work, but it seems to me that I need to ALSO include my second condition (PriceLevel=NONFREE) in the same where clause and I cannot figure out how to do it. Perhaps I can put in an AND for the second condition like this...
AND OrderXML.value('(/Order/InternalInformation/InternalOrderBreakout/InternalOrderHeader/InternalOrderDetails/InternalOrderDetail/PriceLevel/node())[1]','nvarchar(64)') = 'NONFREE'
but I am afraid it will end up operating like an OR since it is an XML query.
Once I get the WHERE clause right I will update the column using a SET like this:
UPDATE Orders SET orderXml.modify('replace value of (/Order/InternalInformation/InternalOrderBreakout/InternalOrderHeader/InternalOrderDetails/InternalOrderDetail/PriceLevel[1]/text())[1] with "NONFREE"')
However, I ran this statement on some test data and none of the XML columns where updated (even though it said zz rows effected).
I have been at this for several hours to no avail. Help is appreciated. Thanks.
if you don't have more than one node with your condition in each row of Orders table, you can use this:
update orders set
data.modify('
replace value of
(
/Order/InternalInformation/InternalOrderBreakout/
InternalOrderHeader/InternalOrderDetails/
InternalOrderDetail[
Item_Number[contains(., "COTRGUID")] and
PriceLevel="NONFREE"
]/PriceLevel/text()
)[1]
with "FREE"
');
sql fiddle demo
If you could have more than one node in one row, there're a several possible solutions, none of each is really elegant, sadly.
You can reconstruct all xmls in table - sql fiddle demo
or you can do your updates in the loop - sql fiddle demo
This may get you off the hump.
Replace #HolderTable with the name of your table.
SELECT T2.myAlias.query('./../PriceLevel[1]').value('.' , 'varchar(64)') as MyXmlFragmentValue
FROM #HolderTable
CROSS APPLY OrderXML.nodes('/InternalOrderDetails/InternalOrderDetail/Item_Number') as T2(myAlias)
SELECT T2.myAlias.query('.') as MyXmlFragment
FROM #HolderTable
CROSS APPLY OrderXML.nodes('/InternalOrderDetails/InternalOrderDetail/Item_Number') as T2(myAlias)
EDIT:
UPDATE
#HolderTable
SET
OrderXML.modify('replace value of (/InternalOrderDetails/InternalOrderDetail/PriceLevel/text())[1] with "MyNewValue"')
WHERE
OrderXML.value('(/InternalOrderDetails/InternalOrderDetail/PriceLevel)[1]', 'varchar(64)') = 'FREE'
print ##ROWCOUNT
Your issue is the [1] in the above.
Why did I put it there?
Here is a sentence from the URL listed below.
Note that the target being updated must be, at most, one node that is explicitly specified in the path expression by adding a "[1]" at the end of the expression.
http://msdn.microsoft.com/en-us/library/ms190675.aspx
EDIT.
I think I've discovered the the root of your frustration. (No fix, just the problem).
Note below, the second query works.
So I think the [1] is some cases is saying "only ~~search~~ the first node".....and not (as you and I were hoping)...... "use the first node..after you find a match".
UPDATE
#HolderTable
SET
OrderXML.modify('replace value of (/InternalOrderDetails/InternalOrderDetail/PriceLevel/text())[1] with "MyNewValue001"')
WHERE
OrderXML.value('(/InternalOrderDetails/InternalOrderDetail/PriceLevel[text() = "NONFREE"])[1]', 'varchar(64)') = 'NONFREE'
/* and OrderXML.value('(/InternalOrderDetails/InternalOrderDetail/Item_Number)[1]', 'varchar(64)') like '%COTRGUID' */
UPDATE
#HolderTable
SET
OrderXML.modify('replace value of (/InternalOrderDetails/InternalOrderDetail/PriceLevel/text())[1] with "MyNewValue002"')
WHERE
OrderXML.value('(/InternalOrderDetails/InternalOrderDetail/PriceLevel[text() = "FREE"])[1]', 'varchar(64)') = 'FREE'
Try this :
;with InternalOrderDetail as (SELECT id,
Tbl.Col.value('Item_Number[1]', 'varchar(40)') Item_Number,
Tbl.Col.value('CountOfNumber[1]', 'int') CountOfNumber,
case
when Tbl.Col.value('Item_Number[1]', 'varchar(40)') like '%COTRGUID'
and Tbl.Col.value('PriceLevel[1]', 'varchar(40)')='NONFREE'
then 'FREE'
else
Tbl.Col.value('PriceLevel[1]', 'varchar(40)')
end
PriceLevel
FROM (select id ,orderxml from demo)
as a cross apply orderxml.nodes('//InternalOrderDetail')
as
tbl(col) ) ,
cte_data as(SELECT
ID,
'<InternalOrderDetails>'+(SELECT ITEM_NUMBER,COUNTOFNUMBER,PRICELEVEL
FROM InternalOrderDetail
where ID=Results.ID
FOR XML AUTO, ELEMENTS)+'</InternalOrderDetails>' as XML_data
FROM InternalOrderDetail Results
GROUP BY ID)
update demo set orderxml=cast(xml_data as xml)
from demo
inner join cte_data on demo.id=cte_data.id
where cast(orderxml as varchar(2000))!=xml_data;
select * from demo;
SQL Fiddle
I have handled following cases :
1. As required both where clause in question.
2. It will update all <Item_Number> like '%COTRGUID' and <PriceLevel>= NONFREE in one
node, not just the first one.
It may require minor changes for your data and tables.

Resources