SQL Server : stored procedure IFNULL check - sql

Solution_id (Primary key, Int)
Col1 (varchar)
Col2 (varchar)
Col3 (varchar)
Col4 (varchar)
Col5 (varchar)
I am writing a stored procedure to update this table. There are 6 input parameters for the above 6 columns.
#Attached_File1 VARCHAR(MAX),
#Attached_File2 VARCHAR(MAX),
#Attached_File3 VARCHAR(MAX),
#Attached_File4 VARCHAR(MAX),
#Attached_File5 VARCHAR(MAX),
#Ticket_ID BIGINT
I want to write a SQL query which will update the table with the values specified in the input parameters. BUT I must not overwrite the attachment columns with null. I mean I need to use only those parameters which contains data.
For example, if the table has a row
[10, "aaa", "bbb", "efg", null, null]
and the input parameters are
(10, null, null, "mno", "ddd", null)
then after the update the row will become
[10, "aaa", "bbb", "mno", "ddd", null]
How to check for null/empty strings and generate the update query accordingly to achieve this?

Is this something like you're after?
UPDATE mytable
SET Col1 = ISNULL(#Attached_File1, Col1),
Col2 = ISNULL(#Attached_File2, Col2),
Col3 = ISNULL(#Attached_File3, Col3),
Col4 = ISNULL(#Attached_File4, Col4),
Col5 = ISNULL(#Attached_File5, Col5)
WHERE Solution_id = #Ticket_ID
ISNULL takes two values, if the first one is not null then it is used, otherwise the 2nd value is used.
See MSDN for more information on ISNULL
Update
I've just noticed your comment at the end, which talks about empty strings...
How to check for null/empty strings and generate the update query accordingly to achieve this?
In which case, you could do the following...
UPDATE mytable
SET Col1 = ISNULL(NULLIF(#Attached_File1,''), Col1),
Col2 = ISNULL(NULLIF(#Attached_File2,''), Col2),
Col3 = ISNULL(NULLIF(#Attached_File3,''), Col3),
Col4 = ISNULL(NULLIF(#Attached_File4,''), Col4),
Col5 = ISNULL(NULLIF(#Attached_File5,''), Col5)
WHERE Solution_id = #Ticket_ID
This uses the NULLIF statement which takes two values, if the first value is the same as the second value, then NULL is returned, otherwise it returns the first value.
See MSDN for more information on NULLIF

update YourTable
set col1 = isnull(#Attached_File1, col1)
, col2 = isnull(#Attached_File2, col2)
, col3 = isnull(#Attached_File3, col3)
, ...
where Solution_ID = #Ticket_ID
If the parameters can contain empty strings, consider #freefaller's answer. If it can contain whitepsace, try:
set col1 = case
when #Attached_File1 like '%[^ \t\r\n]%' then #Attached_File1
else col1
end
, col2 = ...

I would try this:
UPDATE Table
SET
Col1 = ISNULL(#Attached_File1, Col1),
Col2 = ISNULL(#Attached_File1, Col2),
Col3 = ISNULL(#Attached_File1, Col3),
Col4 = ISNULL(#Attached_File1, Col4),
Col5 = ISNULL(#Attached_File1, Col5),
WHERE
Solution_Id = #Ticket_ID

Checking only for Nulls, not for empty strings:
UPDATE
tableX
SET
Col1 = COALESCE(#Attached_File1, Col1),
...
Col5 = COALESCE(#Attached_File5, Col5)
WHERE
Solution_id = #Ticket_ID ;

Related

How to get results fastly with 'column1+column2=#variable'

I am using SQL Server 2016. I have a table more than 10m rows. When I want to search with the query show here, it is very slow.
CREATE TABLE dbo.table_name
(
Col1 int NOT NULL,
Col2 int NULL,
Col3 char(2) NULL,
Col4 char(15) NULL,
Col5 varchar(8) NULL,
Col6 varchar(12) NULL,
Col7 varchar(8) NULL,
Col8 int NULL,
Col9 int NULL,
Col10 varchar(16) NOT NULL
) ON PRIMARY
SELECT
colum3 + column4
FROM
tablename
WHERE
column3 + column4 = #variable
What index method should I use to speed up this query? (of type varchar in two columns)
Maybe you could consider a computed column and indexing that.
ALTER TABLE dbo.tablename ADD concat_column AS
CONCAT(column1 + column2);
CREATE INDEX ix_concat_column ON dbo.tablename(concat_column)
INCLUDE ...
Not sure an index on the separate columns would be of much help, since it would still have to perform the concat on every pair (think 'Rhy' + 'thm' is the same as 'R' + 'hythm').
There's no nice way to do this - but you have a few options.
The problem is that the query has to concatenate col3 and col4 for every row in the database and compare it to your variable. The concatenation means it won't use an index. The time to do that will increase linearly with the number of rows.
Firstly, if you create indexes on column3 and on column4, and change the query to be
select concat(col3, col4)
from table_name
where (
col3 like left(#variable, 3)
or col4 like left(#variable, 3)
and concat(col3, col4) = #variable
you should get a decent improvement in speed. That's because the first where clause can use an index, and should reduce the total number of rows on which the query has to concatenate col3 and col4. I've suggested the first 3 characters of #variable - adjust that to whatever works.
The second option is to create a view which concatenates column3 and column4 into a new column, and create an index on that column. That should be incredibly fast.
The model suggests that Col3 and Col4 values are always 2 and 15 char in lenght.
If this is the case, you could add an index including both column and using them seperatly in the where clause.
It will remains to figure out how Null values should be handled.
CREATE INDEX idx_myindex ON dbo.tablename(Col1, Col2);
GO
SELECT
Concat(colum3, column4)
FROM
tablename
WHERE
column3 = left(#variable,2) and (column4 = right(#variable,15)
Solution handling Null values based on the same assumptions.
SELECT
Concat(colum3, column4)
FROM
tablename
WHERE
( Len(#variable) = 17 AND
column3 = left(#variable,2) and (column4 = right(#variable,15) )
OR
( Len(#variable) = 2 AND
column3 = #variable AND column4 is null )
OR
( Len(#variable) = 15 AND
column3 is null AND column4 = #variable )

Copy rows that has atleast one null value in any column in SQL Server

I have table_1 that has certain columns. I want to copy that data to table_2 that has atleast one null value in any column of table_1. For e.g. table_1 has three columns. Now, I want to copy those rows to table_2 that has atleast one null value in any of the three columns of table_1.
I tried it using following query:
insert into table_2 (col1, col2, col3)
select col1, col2, col3
from table_1
where col1 is null or col2 is null or col3 is null
But, there is an issue that table_2 has a column 'error_value' which should contain data that indicates which column(s) has NULL value corresponding to that particular row like it should mention 'col2 is null' if col2 has missing value in that row. If more than one column has NULL values, then it should mention about all those columns in 'error_value' column like 'col1, col2, col3' is null if all columns have missing values.
Any suggestions or help how can I implement it.
Use CASE expressions to get the null columns, then concatenate. As of SQL Server 2017 this can best be achieved with CONCAT_WS (https://learn.microsoft.com/de-de/sql/t-sql/functions/concat-ws-transact-sql?view=sql-server-ver15).
insert into table_2 (col1, col2, col3, error_value)
select
col1, col2, col3,
concat_ws(', ',
case when col1 is null then 'col1' end,
case when col2 is null then 'col2' end,
case when col3 is null then 'col3' end
) + ' is null'
from table_1
where col1 is null or col2 is null or col3 is null;
Update
2017 version introduced concat_ws which can simplify the code significantly - check out Thorsten Kettner's answer for details.
I'm leaving this answer here in the hope it will help some other reader that uses an older version of SQL Server.
First version
One simple solution would be to use a combination of case, concat, stuff, and the string concatenation operator (+), taking advantage of the fact that concat will implicitly convert null to empty strings, while + will not.
First, create and populate sample table (Please save us this step in your future questions):
create table table_1 (col1 int, col2 int, col3 int);
create table table_2 (col1 int, col2 int, col3 int, error_value varchar(100));
insert into table_1(col1, col2, col3) VALUES
(null, null, null),
(null, null, 1),
(null, 1, null),
(null, 1, 1),
(1, null, null),
(1, null, 1),
(1, 1, null),
(1, 1, 1);
Then, the insert...select statement:
insert into table_2 (col1, col2, col3, error_value)
select
col1, col2, col3, stuff(
concat(
',' + case when col1 is null then 'col1' end, -- will be null if col1 contains a value
',' + case when col2 is null then 'col2' end, -- will be null if col2 contains a value
',' + case when col3 is null then 'col3' end, -- will be null if col3 contains a value
' is null'), 1, 1, '')
from table_1
where col1 is null or col2 is null or col3 is null
See a live demo on rextester
How about below query using CONCAT and IIF:
insert into
table_2 (col1, col2, col3, col4)
select
col1,
col2,
col3,
CONCAT(
IIF(col1 is null, 'col1 ', ''),
IIF(col2 is null, 'col2 ', ''),
IIF(col3 is null, 'col3 ', ''),
' is null'
)
from
table_1
where
col1 is null
or col2 is null
or col3 is null
Tried with simple case statement and concat
INSERT INTO #table_2 (col1, col2, col3,error_value)
SELECT col1, col2, col3
,SUBSTRING(CONCAT( CASE WHEN col1 IS NULL THEN ',col1' ELSE '' END
,CASE WHEN col2 IS NULL THEN ',col2' ELSE '' END
,CASE WHEN col3 IS NULL THEN ',col3' ELSE '' END
),2,20) + ' is null'
FROM #table_1
WHERE col1 IS NULL OR col2 IS NULL OR col3 IS NULL

Showing the result of COALESCE into separate columns based from where they where retrieved

I have a table with many NULL values. Therefore I use the COALESCE function to retrieve the NON NULL values. This works fine when the result of the COALESCE is to be placed in a single Column. However I need to place the values of the COALESCE into separate Columns depending from where they where picked.
E.g. I have the following table.
SELECT COALESCE(Col1, Col2, Col3, Col4) FROM Table 1
Will produce:-
Column1
1
1
3
4
However I do not want that result but I want this result:-
Col1 Col2 Col3 Col4
1 - - -
- 1 - -
- - 3 -
- 4 - -
As you can see I want only one field populated (that why I'm suing COALESCE but the result of COALESCE should be placed as illustrated, NOTICE ONE VALUE PER ROW.
Any ideas of how I can achieve this result please.
coalesce can be built with case statements. You need something like the below:
select col1
, case when col1 is not null then null else col2 end 'Col2'
, case when col1 is not null or col2 is not null then null else col3 end 'Col3'
, case when col1 is not null or col2 is not null or col3 is not null then null else col4 end 'Col4'
from table
You can achieve this with a combination of PIVOT, UNPIVOT and ROW_NUMBER.
declare #t table(rn int identity(1,1) primary key, col1 int, col2 int, col3 int, col4 int);
insert #t values (1,null,null,null), (null,1,0,null), (null,null,3,null), (null,4,null,2);
with a as (
select *, ranking = row_number() over (partition by rn order by col)
from #t a
unpivot ([val] for [col] in ([col1],[col2],[col3],[col4])) p
)
select *
from a
pivot (min(val) for [col] in ([col1],[col2],[col3],[col4])) p
where ranking = 1

Can CHECK constraints act like if else?

I have a table with 4 columns:
(ID (PK, int, NOT NULL), col1 (NULL), col2 (NULL), col3 (NULL))
I'd like to add a CHECK constraint (table-level I think?) so that:
if col1 OR col2 are NOT NULL then col3 must be NULL
and
if col3 is NOT NULL then col1 AND col2 must be NULL
i.e. col3 should be null if col1 and col2 are not null or vice-versa
I am very new to SQL and SQL server though and am not sure how to actually implement this or even if it can/should be implemented?
I think maybe:
CHECK ( (col1 NOT NULL OR col2 NOT NULL AND col3 NULL) OR
(col3 NOT NULL AND col1 NULL AND col2 NULL) )
But I am not sure if the brackets can be used to group the logic like this? If not, how can this best be implemented?
Absolutely, you can do this. See this sqlfiddle.
However, you need to make sure you bracket your logic properly. You should never mix ANDs and ORs in the same bracketing scope. So:
(col1 NOT NULL OR col2 NOT NULL AND col3 NULL)
Needs to become:
((col1 NOT NULL OR col2 NOT NULL) AND col3 NULL)
Or:
(col1 NOT NULL OR (col2 NOT NULL AND col3 NULL))
Depending on your intent.
Just be careful not to make mistake with brackets.
CREATE TABLE Test1 (col1 INT, col2 INT, col3 INT);
ALTER TABLE Test1
ADD CONSTRAINT CHK1
CHECK (((col1 IS NOT NULL OR col2 IS NOT NULL) AND col3 IS NULL) OR
((col1 IS NULL AND col2 IS NULL) AND col3 IS NOT NULL))
INSERT INTO Test1 VALUES (1,1,1); --fail
INSERT INTO Test1 VALUES (1,1,NULL); --good
INSERT INTO Test1 VALUES (1,NULL,NULL); --good
INSERT INTO Test1 VALUES (1,NULL,1); --fail
INSERT INTO Test1 VALUES (NULL,NULL,1); --good
I would say create a UDF like below
create FUNCTION dbo.fn_check_val
(#col1 int , #col2 int , #col3 int)
RETURNS bit
AS
BEGIN
declare #toRet bit
IF(#col1 is Not null OR #col2 is NOT NULL)
Begin
if(#col3 is null)
Begin
Set #toRet = 1
End
Else
Begin
Set #toRet = 0
End
End
Else
if(#col3 is not null)
Begin
Set #toRet = 1
End
Else
Begin
Set #toRet = 0
End
return #toRet
END
and then add following check statement in your table
([dbo].[fn_check_val]([col1],[col2],[col3])=(1))

SQL Null values and non-Null values

I have a table that looks like this:
ID (pk,int)
Col1 (nvarchar)
Col2 (nvarchar)
Col3 (nvarchar)
In all columns (except ID) some values are NULL.
I want to make a query that will look like this:
SELECT * FROM Table
WHERE (Col1=<parameter> AND Col1 IS NULL)
+ (Col2=<parameter> AND Col2 IS NULL)
etc.
I need values that are NULL and that are equal to parameter
Thanks
Your question, specifically the bit "I need values that are NULL and that are equal to parameter", makes no sense. The where clause:
Col1 = <parameter> AND Col1 IS NULL
will never be true, since a column is either NULL or something. It can't be both at the same time.
If you mean you want values that are equal to the parameter OR NULL, you should use:
Col1 = <parameter> OR Col1 IS NULL
Replace the AND with OR in your WHERE statements.
WHERE (Col1=<parameter> OR Col1 IS NULL)
ANd (Col2=<parameter> OR Col2 IS NULL)