This question would be addendum on the last answer in T-SQL stored procedure that accepts multiple Id values
I am passing a few list of ids as a parameter to a stored procedure. Each of them default to null if no data is sent in. For instance, I want food products with ids 1, 2, 5, 7, 20 returned by my stored procedure. I also send in a list of color ids, and production location ids. I am passing in a comma delimited list of these ids. Similar to the last answer in the question referenced above, I create a temp table with the data from each of the parameters. I then want to have a select statement that would be something like this:
SELECT * FROM Candies
INNER JOIN #TempColors
ON Candies.ColorsID = #TempColors.ColorID
INNER JOIN Locations
ON Candies.LocationID = Locations.LocationID
This only works when the parameters are populated and LEFT OUTER JOINS will not filter properly. What is the way to filter while accepting null as a valid parameter?
You could use
some join condition OR #param IS NULL
in your join, it would return all results if a null was supplied - though as far as I can see you don't specify what behaviour you want when null is passed
(when I say param I mean temp table column doing this on my phone and it's not easy ;))
Edit:
This one worked for me:
http://sqlfiddle.com/#!3/c7e85/26
e.g.
-- Assume this is your values string which is populating the table
DECLARE #Values varchar(50)
-- Your code to populate the table here: assume the string is NULL when no values are passed
INSERT INTO #TempColors BLAH BLAH...
-- Select statement
SELECT * FROM Candies
LEFT JOIN #TempColors
ON Candies.ColorsID = #TempColors.ColorID
WHERE 1 = CASE
WHEN Candies.ColorsID IS NULL AND #Values IS NULL THEN 1
WHEN Candies.ColorsID IS NOT NULL AND #Values IS NOT NULL THEN 1
ELSE 0
END
This way the NULLs will be filtered out with a NON-NULL parameter, but kept in for a NULL parameter
Related
I have a stored procedure with a few parameters. One of them is a varchar containing a possible list of IDs (comma separated values i.e. 1, 2, 5, 9). I need the procedure to ignore the parameter when it is NULL. This is the way I actually do:
CREATE PROCEDURE PROC_TEST
#MAIN_ID INT = NULL,
#DETAIL_IDs VARCHAR(2000)
AS
BEGIN
SELECT ..... FROM TABLE1 INNER JOIN TABLE2 ON ...
WHERE
TABLE1.ID = ISNULL(#MAIN_ID, TABLE1.ID) AND
(
#DETAIL_IDs IS NULL
OR TABLE2.ID IN
(SELECT VALUE FROM STRING_SPLIT(#DETAIL_IDs,','))
)
END
It seems like the #DETAIL_IDs IS NULL part of the code requires a lot of time to execute: What am I doing wrong here?
Even if I remove the OR clause and simply add AND #DETAIL:IDs IS NULL it takes a long time.
The tables have more than 1 million records each.
I am inserting data from one table to another so when inserting I got above error mentioned in title
Insert into dbo.source(
title
)
Select
Title from dbi.destination
title in dbo.source table is of INT data type and title in dbo.destination table is of Varchar data type and I have data like abc, efg, etc. in the dbo.destination table.
So how to solve this now or is it possible to convert and insert values?
You can use SQL Server try_cast() function as shown below. Here is the official documentation of TRY_CAST (Transact-SQL).
It Returns a value cast to the specified data type if the cast succeeds; otherwise, returns null.
Syntax
TRY_CAST ( expression AS data_type [ ( length ) ] )
And the implementation in your query.
INSERT INTO dbo.source (title)
SELECT try_cast(Title AS INT)
FROM dbi.destination
Using this solution you need to be sure you have set the column allow null true otherwise it will give error.
If you do not want to set the allow null then you need minor changes in select query as shown below - passing the addition criteria to avoid null values.
Select ... from ... where try_cast(Title AS INT) is not null
You must use isnumeric method of SQL for checking is data numeric or not
CONVERT(INT,
CASE
WHEN IsNumeric(CONVERT(VARCHAR(12), a.value)) = 1 THEN CONVERT(VARCHAR(12),a.value)
ELSE 0 END)
Think about your data types - obviously you cannot have a text string like 'abc' in a column that is defined to hold integers.
It makes no sense to copy a string value into an integer column, so you have to confirm how you want to handle these - do you simply discard them (what is the impact of throwing data away?) or do you replace them with some other value?
If you want to ignore them and use NULL in place then use:
INSERT dbo.Source (Title)
SELECT CASE
WHEN ISNUMERIC(Title) = 1 THEN CAST(Title as INT)
ELSE NULL
END
FROM dbo.Destination
If you want to replace the value then simply change NULL above to the value you want e.g. 0
You can use regex to root out non numeric characters
Insert into dbo.source(
title
)
Select
case when Title not like '%[^0-9]%' then null else cast(Title as int) end as Title
from dbi.destination
Just filter only numeric field from destination table like as below:
Insert into dbo.source(
title
)
Select
Title from dbi.destination
where ISNUMERIC(Title) = 1
I have some specific set of values that I want to filter on a column, I don't want to do an 'in' clause in SQL Server. I want to use loop to pass in different set of values each time.
For example if there is a name column in my data, and I want to run query 5 times with different filter value.
Please look at the loop query attached below.
DECLARE #cnt INT = 1;
WHILE #cnt < 94
BEGIN
SELECT Name, COUNT(*) AS Number_of_Names
FROM Table
WHERE name IN ('John')
AND value IS NOT NULL
GROUP BY Name
SET #cnt = #cnt + 1;
END;
I want to pass in different values under 'name' column at each loop like john in the case above, then mary in the next loop likewise based on set of values I pass in the variable like #values = John,Mary,Nicole,matt etc..
Considering the comments on your question, this should give you an idea on how to achieve a solution without using loops and still get all the names even when the name is not present on the table.
SELECT Name,
COUNT(value) AS Number_of_Names --Only count when value is not null
FROM (VALUES('John'), ('Mary'), ('Nicole'), ('Matt'))Names(name) --This can be replaced by a table-valued parameter or temp table.
LEFT JOIN Table t ON Names.name = t.name
--WHERE name IN ('John') /*No longer needed*/
--AND value IS NOT NULL /*Removed this because it would make the OUTER JOIN behave as an INNER JOIN*/
GROUP BY Name;
I am new to sql. I want some thing like
DECLARE #VALID_ITEM_NUMBERS ITEM_NUMBER
SELECT #ITEM_NUMBERS = ITEM_NUMBER FROM [dbo].[ITEM] where IS_VALID = 1
(
here in the first line ITEM_NUMBER is a predefined type,
and on the second line ITEM_NUMBER refers to a column (of type ITEM_NUMBER) in the table named ITEM. IS_VALID is also a column in ITEM table
)
But SET or SELECT returns only one value. I want #VALID_ITEM_NUMBERS to contain all the valid item numbers, like an array.
Is there any way to do this without crating a separate stored procedure?
create one table variable to store all the values
declare #ITEM_NUMBERS table (ITEM_NUMBER int NOT NULL)
insert into #ITEM_NUMBERS(ITEM_NUMBER)
(select ITEM_NUMBER FROM [dbo].[ITEM] where IS_VALID = 1 )
select ITEM_NUMBER from #ITEM_NUMBERS
Okay so I have spent some time researching this but cannot seem to find a good solution.
I am currently creating a stored procedure that takes a set of optional parameters. The stored procedure will act as the "universal search query" for multiple tables and columns.
The stored procedure looks something like this (Keep in mind that this is just a stripped down version and the actual stored procedure has more columns etc.)
The '#ProductIdsParam IntList READONLY' is an example table valued parameter that I would like to JOIN if it is not empty. In other words, the query should only search by parameters that are not null/empty.
Calling the procedure and parsing the other parameters works just like it should. I might however have misunderstood and should not do a "universal search query" like this at all.
CREATE PROCEDURE [dbo].[usp_Search]
#ProductIdParam INT = NULL,
#CustomerNameParam NVARCHAR(100) = NULL,
#PriceParam decimal = NULL,
-- THIS IS WHAT I'D LIKE TO JOIN. BUT THE TABLE CAN BE EMPTY
#ProductIdsParam IntList READONLY
AS
BEGIN
SET NOCOUNT ON;
SELECT DISTINCT
CustomerTransactionTable.first_name AS FirstName,
CustomerTransactionTable.last_name AS LastName,
ProductTable.description AS ProductDescription,
ProductTable.price as ProductPrice
FROM dbo.customer AS CustomerTransactionTable
-- JOINS
LEFT JOIN dbo.product AS ProductTable
ON CustomerTransactionTable.product_id = ProductTable.id
WHERE
(ProductTable.id = #ProductIdParam OR #ProductIdParam IS NULL)
AND (CustomerTransactionTable.first_name = #CustomerNameParam OR #CustomerNameParam IS NULL)
AND (CustomerTransactionTable.price = #PriceParam OR #PriceParam IS NULL)
END
You can add the int table in LEFT join and then add a where condition based on the record count in the filter table. If #ProductIdsParam is declared as table, you should first count records in it and store the result in a varaible.
AND COALESCE(#ProductIdsParam.id, 0) = (CASE WHEN #ProductIdsCount = 0 THEN 0 ELSE ProductTable.id END)
In case #ProductIdsCount = 0 then you get always 0 = 0 so you get all the records, else you select only records where the productId in the filter table equals the ProductTable.id.
There are other (maybe cleaner) approaches possible though but I think this works.