Set int var value as wildcard - sql

I've looked into this and I'm just struggling to accept that it isn't possible to do.
I write queries for others to use and normally include some declared variables at the top so the user can "filter" their search at the top of the query and not bother with the rest.
Sometimes the user may not want to specify the values for a particular variable, as they want to return all types. I know this isn't what SQL is for and that there are reporting tools. Any suggestions on how to do this?
DECLARE #foo int, #bar bit
SET #foo = *
SET #bar = *
SELECT *
FROM table
WHERE foo = #foo
AND bar = #bar

* isn't a valid int value and even if an int value could be used as a wildcard, you would need to use LIKE not =.
What you want here is a NULL value and the proper boolean logic.
SELECT {Columns}
FROM [Table]
WHERE (foo = #foo OR #foo IS NULL)
AND (bar = #bar OR #bar IS NULL)
OPTION (RECOMPILE);-- Comment out/remove if you aren't using T-sQL due to incorrect tagging

Related

Selecting a value from a table into a variable without changing the formatting?

Using code similar to the below within a larger stored procedure:
DECLARE #Foo NVARCHAR(500)
SET #Foo = (SELECT [MyColumn] FROM [MyTable] WHERE ID = 1) -- May be numeric or text
SELECT * FROM [MyTable] WHERE [MyColumn] = #Foo
In theory, the above code should always return the same as the query
SELECT * FROM [MyTable] WHERE ID = 1
However, it often returns (0 Rows). The reason for this, is that the variable #Foo does not always store the value found in the same format as the table.
For example, the table may contain the value 2152437. However, the select statement above would cause this to be stored as #Foo = 2.01512e+007.
I have attempted to solve this problem by using the code
IF ISNUMERIC((SELECT [MyColumn] FROM [MyTable] WHERE ID = 1)) = 1 SET #Foo = Str((SELECT [MyColumn] FROM [MyTable] WHERE ID = 1))
ELSE SET #Foo = (SELECT [MyColumn] FROM [MyTable] WHERE ID = 1)
The problem with this is that, although it allows large integers to be stored in #Foo in the same format as they are found in the database, it causes decimals to be rounded. I have also tried using the code
SET #Foo = Str((SELECT [MyColumn] FROM [MyTable] WHERE ID = 1), 10, 6)
However, this adds unwanted 0's to the end of the value found. Thus, this is also not an option.
How might I preserve the formatting used within the database when storing a selected value into a variable - given that the value found in the database may or may not be numeric?
However, when decimals are found
It is not an exact solution, but you could try:
SELECT *
FROM [MyTable]
WHERE CAST([MyColumn] as NVARCHAR(500)) = #Foo;
However, I really question what you are trying to do. Why are you saving a numeric value in a variable?
Second, the problem that you are getting is because the column is stored using a floating point representation. Your code will work as you intend if you switch to a fixed-point representation (numeric/decimal). That might be the simplest fix to this problem.

Select and assign to variable in one statement?

I'd like to display the results of a query, and I'd like to capture a column's value at the same time. The FROM and WHERE are the same in both queries. Can I do both in one query, or is it easier/better to just do the query twice, like this?
DECLARE #fooId INT
SET #fooId = (SELECT FooId FROM Bar WHERE SnuhId = 5)
SELECT * FROM Bar WHERE SnuhId = 5
Unfortunately it has to be done in two operations.
Test:
DECLARE #VAR DATETIME
SELECT #VAR=GETDATE(), GETDATE()
Yields error message 141.
Here's another SO post on this.
If you try to do it in one query, you will get an error msg - A SELECT statement that assigns a value to a variable must not be combined with data-retrieval operations.
To avoid this, the possibilities are
1) Make sure that all columns are assigned to a local variable. Ex
DECLARE #fooId INT
DECLARE #barId INT
SELECT #fooId = FooId, #barId = BarId FROM Bar WHERE SnuhId = 5
2) simply remove '*' from the SELECT statement
SELECT #fooId = FooId, BarId FROM Bar WHERE SnuhId = 5 //remove BarId column from this query
3) If you really need to do both, meaning to assign the value to local variables and to return the columns as a result set, you have to do it in 2 steps instead of combining them into one SELECT statement.
You can do the following to reduce one line:
DECLARE #fooI INT
SET #fooId = (SELECT FooId FROM Bar WHERE SnuhId = 5)
SELECT #fooId as [FooId]
It will reduce unnecessary replication of your query.
I hope this helps.

What is the best way to equate NULL columns in SQL?

I am performing a MERGE between two tables
MERGE indexdecomp.Constituent targ
USING (SELECT ic.ConstituentName
FROM indexdecomp.IndexConstituents ic) src
ON (((targ.Name = src.ConstituentName) OR (targ.Name IS NULL AND src.ConstituentName IS NULL)))
WHEN NOT MATCHED BY TARGET THEN
UPDATE SET
targ.Name = src.ConstituentName
;
and in my ON clause I have the following predicate:
(targ.Name = src.ConstituentName) OR (targ.Name IS NULL AND src.ConstituentName IS NULL)
I have this predicate since I am considering it a match if both Names are equal or if both names are `null.
Is there a better or more conventional way to handle equality between two null columns? What way would produce the quickest execution?
You can do something like this: (SQL ref)
SET ANSI_NULLS OFF;
MERGE indexdecomp.Constituent targ
USING (SELECT ic.ConstituentName
FROM #IndexConstituents ic) src
ON (((targ.Name = src.ConstituentName)))
WHEN NOT MATCHED BY TARGET THEN
UPDATE SET
targ.Name = src.ConstituentName;
SET ANSI_NULLS ON;
But that seems to be a pretty heavy trade off for lumping off a predicate and neither is very readable. You could actually abstract this mess with a UDF that takes two string arguments and returns a boolean.
Something like:
create function StrNullCompare(#a varchar(max), #b varchar(max))
returns int
as
begin
if ((#a = #b) or (#a is null and #b is null)) return 1;
return 0;
end
-- tests
select dbo.StrNullCompare('wer', 'were');
select dbo.StrNullCompare('wer', 'wer');
select dbo.StrNullCompare('hi', null);
select dbo.StrNullCompare(null, null);
And your predicate becomes:
(dbo.StrNullCompare(targ.Name, src.ConstituentName)=1)
You could try..
ISNULL (targ. Name,'a magic string value') =ISNULL (src.ConstituentName,'a magic string value')
Of course add your own magic string as appropriate for example use newid () to get a guid and use that.
Not really sure if this is "better" than an and or but is a little more human readable; worth benchmarking and testing both approaches though.

Set a parameter in select statement

I am trying to set a parameter in the select statement and then pass it to a user defined function in the same statement. Is this possible? If yes, where is my mistake? If no, then I guess I will have to write a cursor and some temporary tables which I want to avoid.
declare #param varchar(1000)
select Pincode, Name,( #param = AlternateName) as AlternateName
,(select Top(1) part from SDF_SplitString (#param,',')) as NewName from PinCodeTable
You can either get all of the fields out as variables, or get the usual set of rows, but you can't mix and match in a single SELECT. Variables are limited to queries that return no more than one row. (Why do I feel like I'm about to learn something frightening?)
If you are writing a stored procedure and doing something like header/trailer rowsets, you can always return a rowset built from variables:
SELECT #Foo = Foo, #Bar = Bar, ... from Headers where Id = 42
SELECT #Foo as Foo -- Return the first rowset.
SELECT Size, Weight, Color from Trailers where Bar = #Bar -- Return second rowset.

Complex query filter using Like() in T-SQL

I'm writing a SQL script that we want our accounting team to be able to edit, without dealing with engineering.
The general idea is to have a .sql script, which defines some variables at the top of the query, and then has several complex queries below it that use those variables.
The problem we have is that we want the accounting team to be able to specify the filter to use. For example:
DECLARE #year INT
DECLARE #month INT
DECLARE #filter VARCHAR(30);
SET #year = 2010
SET #month = 7
SET #filter = '%test%'
Here the team can change the month and the year that the subsequent queries return. They can also define ONE filter element, in this example, excluding any records where the username has the string 'test' in it.
My question is whether or not there is a way to specify OR's to a LIKE(). Eg, ideally we'd have the #filter variable as something like '%test%, or %other%. Now I know that's not real syntax, but I'm wondering if there is syntax that lets me achieve that. I've scowered MSDN on the LIKE() syntax with no joy. Should I use some different query expression?
Probably the simplest thing to do would be to just have multiple parameters, though it's not pretty:
SET #filter_1 = '%test%'
SET #filter_2 = '%foo%'
SET #filter_3 = '%'
SET #filter_4 = '%'
SELECT *
FROM BAR
WHERE var LIKE #filter_1
OR var LIKE #filter_2
OR var LIKE #filter_3
OR var LIKE #filter_4
OR var LIKE #filter_5
By defaulting them to %, they will always match by default.
You could also use dynamic SQL and a local table variable. Basically, create a local table with one column, allow them to change the INSERT statements into that table, then define a loop that iterates over the contents of that table to dynamically generate the LIKE clauses. It would work, but it would be a bit more code. The example above is quick and dirty, but I'd guess it's probably sufficient for what you need to do.
I'd use a join with a LIKE predicate. You can execute the following code sample in a query window to see how this works:
DECLARE #tblFilter TABLE
(sFilter nvarchar(MAX) NOT NULL);
INSERT #tblFilter
SELECT * FROM (VALUES ('%one%'), ('%two%'), ('%three%')) v(s);
DECLARE #tblData TABLE
(iId int NOT NULL PRIMARY KEY IDENTITY,
sData nvarchar(MAX));
INSERT #tblData(sData)
SELECT * FROM (VALUES ('one'), ('two three'), ('four')) v(s);
SELECT DISTINCT iId
FROM #tblData d
JOIN #tblFilter f ON d.sData LIKE f.sFilter;
I assume that the different query strings are in the #tblFilter table, which could be a TVP, coming from XML values, from comma-separated values, from a temp table or whatever.