How to replace one value from a column - sql

I have a script that pulls a column and some of the cells have nothing so they are returned with a 'dash'. I want these to be replaced with 'Global' but have all the other results retrieved. I don't want to specify each one in a case as they can change from time to time.

Something like this, perhaps?
SELECT CASE
WHEN MyColumn = '-' THEN 'Global'
ELSE MyColumn
END
FROM MyTable

If you can, have a lookup table. Something like
create table ConfigTable
(
pkConfigTable int identity(1, 1) not null primary key clustered,
ConfigKey varchar(100) not null,
ConfigValue varchar(100) not null
)
go
insert into ConfigTable
values('NothingString', '-')
go
declare #NullChar varchar(10)
go
select #NullChar = ConfigValue
from ConfigTable
where ConfigKey = 'NothingString'
go
select
case
when yourColumnToTest = #NullChar
then 'Global'
else yourColumnToTest
end
from yourTable

A simple IF could possibly do:
SELECT
IF(Column = '-', 'Global', Column) AS NiceNameForColumn
FROM
Table

SELECT COALESCE(NULLIF(a_column, '-'), 'Global') AS a_column_narrative
FROM YourTable;

Related

Append String without repeating CASE expression?

How can I append a string to a field based on a condition without having to repeat the same case statement multiple times in the SELECT?
For example.
CASE WHEN t1.location = A THEN '1111' ELSE '2222' + t1.telephone1,
CASE WHEN t1.location = A THEN '1111' ELSE '2222' + t1.telephone2
If I had to select 10 columns this way I would have to copy paste 10 times. I was thinking of a possible variable or CTE but that seems like I would have to repeat the case anyway?
You should be able to accomplish this with a CTE:
with t1_as_cte (myexpression,telephone1,telephone2)
as (
select CASE WHEN t1.location = A THEN '1111' ELSE '2222' end myexpression,
t1.telephone1,
t1.telephone2
from mytable t1
)
select myexpression + telephone1, myexpression + telephone2
from t1_as_cte
So if you have 10 phone number columns (for some reason) that you want to prefix all of them based on location, then use CROSS APPLY VALUES.
DROP TABLE IF EXISTS #Test;
CREATE TABLE #Test
(
TestID TINYINT IDENTITY PRIMARY KEY
,[Location] VARCHAR(1)
,Phone1 VARCHAR(15)
,Phone2 VARCHAR(15)
,Phone3 VARCHAR(15)
,Phone4 VARCHAR(15)
,Phone5 VARCHAR(15)
,Phone6 VARCHAR(15)
,Phone7 VARCHAR(15)
,Phone8 VARCHAR(15)
,Phone9 VARCHAR(15)
,Phone10 VARCHAR(15)
)
;
INSERT INTO #Test
([Location],Phone1,Phone2,Phone3,Phone4,Phone5,Phone6,Phone7,Phone8,Phone9,Phone10)
VALUES
('A','PhoneA','PhoneA','PhoneA','PhoneA','PhoneA','PhoneA','PhoneA','PhoneA','PhoneA','PhoneA')
,('B','PhoneB','PhoneB','PhoneB','PhoneB','PhoneB','PhoneB','PhoneB','PhoneB','PhoneB','PhoneB')
;
SELECT
TST.TestID
,Phone1 = CONCAT(PFX.Prefix,TST.Phone1)
,Phone2 = CONCAT(PFX.Prefix,TST.Phone2)
,Phone3 = CONCAT(PFX.Prefix,TST.Phone3)
,Phone4 = CONCAT(PFX.Prefix,TST.Phone4)
,Phone5 = CONCAT(PFX.Prefix,TST.Phone5)
,Phone6 = CONCAT(PFX.Prefix,TST.Phone6)
,Phone7 = CONCAT(PFX.Prefix,TST.Phone7)
,Phone8 = CONCAT(PFX.Prefix,TST.Phone8)
,Phone9 = CONCAT(PFX.Prefix,TST.Phone9)
,Phone10 = CONCAT(PFX.Prefix,TST.Phone10)
FROM #Test AS TST
CROSS APPLY
(
VALUES(CASE WHEN TST.[Location] = 'A' THEN '1111' ELSE '2222' END)
) AS PFX (Prefix)
You can use a function. Or you can use Cross Apply and Outer Apply

Show other column value if the value of the column is NULL or blank

I want to display value of other column if the value of my column is NULL or blank. Below is my table.
DECLARE #Tab TABLE(ID INT, suser VARCHAR(10), sgroup VARCHAR(10), sregion VARCHAR(10))
INSERT INTO #Tab VALUES(1,'Test',NULL,NULL),(2,'','Group',NULL),(3,NULL,NULL,'Region'),(4,NULL,NULL,NULL)
SELECT * from #Tab
My Query:
SELECT ID
,Case WHEN suser IS NULL OR suser = ''
THEN sgroup
WHEN sgroup IS NULL OR sgroup = ''
THEN sregion
ELSE NULL
END AS col
from #Tab
I want oupput as:-
DECLARE #Tab1 TABLE(ID INT, col VARCHAR(10))
INSERT INTO #Tab1 VALUES(1,'Test'),(2,'Group'),(3,'Region'),(4,NULL)
SELECT * from #Tab1
Thanks
Blank and NULL are not the same. If you want to treat '' and NULL as the same value, one method would be to use NULLIF:
ISNULL(NULLIF(YourFirstColumn,''),YourOtherColumn)
Ideally, however, if either could be stored in your data but they should be treated as the same, don't allow one of them. Personally, I would update all the values of the column to NULL where they have a value of '' and then add a constraint that doesn't allow the value ''. Something like:
UPDATE YourTable
SET YourColumn = NULL
WHERE YourColumn = '';
ALTER TABLE YourTable ADD CONSTRAINT YourColumn_NotBlank CHECK (YourColumn IS NULL OR YourColumn <> '');
use COALESCE function it will return 1st non null value
SELECT ID ,COALESCE(suser , sgroup, sregion)
col
from #Tab

COLLATE in UDF does not work as expected

I have a table with text field. I want to select rows where text is in all caps. This code works as it should, and returns ABC:
SELECT txt
FROM (SELECT 'ABC' AS txt UNION SELECT 'cdf') t
WHERE
txt COLLATE SQL_Latin1_General_CP1_CS_AS = UPPER(txt)
then I create UDF (as suggested here):
CREATE FUNCTION [dbo].[fnsConvert]
(
#p NVARCHAR(2000) ,
#c NVARCHAR(2000)
)
RETURNS NVARCHAR(2000)
AS
BEGIN
IF ( #c = 'SQL_Latin1_General_CP1_CS_AS' )
SET #p = #p COLLATE SQL_Latin1_General_CP1_CS_AS
RETURN #p
END
and run it as follows (which looks like an equivalent code to me):
SELECT txt
FROM (SELECT 'ABC' AS txt UNION SELECT 'cdf') t
WHERE
dbo.fnsConvert(txt, 'SQL_Latin1_General_CP1_CS_AS') = UPPER(txt)
however, this returns ABC as well as cdf.
Why is that so, and how do I get this to work?
PS I need UDF here to be able to call case-sensitive comparison from .Net LINQ2SQL provider.
A variable cannot have it's own collation. It will always use the server's default. Check this:
--I declare three variables, each of which get's its own collation - at least one might think so:
DECLARE #deflt VARCHAR(100) = 'aBc'; --Latin1_General_CI_AS in my system
DECLARE #Arab VARCHAR(100) = 'aBc' COLLATE Arabic_100_CS_AS_WS_SC;
DECLARE #Rom VARCHAR(100) = 'aBc' COLLATE Romanian_CI_AI
--Now check this. All three variables are seen as the system's default collation:
SELECT [name], system_type_name, collation_name
FROM sys.dm_exec_describe_first_result_set(N'SELECT #deflt AS Deflt, #Arab AS Arab, #Rom AS Rom'
,N'#deflt varchar(100), #Arab varchar(100),#Rom varchar(100)'
,0);
/*
name system_type_name collation_name
Deflt varchar(100) Latin1_General_CI_AS
Arab varchar(100) Latin1_General_CI_AS
Rom varchar(100) Latin1_General_CI_AS
*/
--Now we check a simple comparison of "aBc" against "ABC"
SELECT CASE WHEN #deflt = 'ABC' THEN 'CI' ELSE 'CS' END AS CheckDefault
,CASE WHEN #Arab = 'ABC' THEN 'CI' ELSE 'CS' END AS CheckArab
,CASE WHEN #Rom = 'ABC' THEN 'CI' ELSE 'CS' END AS CheckRom
/*CI CI CI*/
--But we can specify the collation for one given action!
SELECT CASE WHEN #deflt = 'ABC' THEN 'CI' ELSE 'CS' END AS CheckDefault
,CASE WHEN #Arab = 'ABC' COLLATE Arabic_100_CS_AS_WS_SC THEN 'CI' ELSE 'CS' END AS CheckArab
,CASE WHEN #Rom = 'ABC' COLLATE Romanian_CI_AI THEN 'CI' ELSE 'CS' END AS CheckRom
/*CI CS CI*/
--But a table's column will behave differently:
CREATE TABLE #tempTable(deflt VARCHAR(100)
,Arab VARCHAR(100) COLLATE Arabic_100_CS_AS_WS_SC
,Rom VARCHAR(100) COLLATE Romanian_CI_AI);
INSERT INTO #tempTable(deflt,Arab,Rom) VALUES('aBc','aBc','aBc');
SELECT [name], system_type_name, collation_name
FROM sys.dm_exec_describe_first_result_set(N'SELECT * FROM #tempTable',NULL,0);
DROP TABLE #tempTable;
/*
name system_type_name collation_name
deflt varchar(100) Latin1_General_CI_AS
Arab varchar(100) Arabic_100_CS_AS_WS_SC
Rom varchar(100) Romanian_CI_AI
*/
--This applys for declared table variables also. The comparison "knows" the specified collation:
DECLARE #TableVariable TABLE(deflt VARCHAR(100)
,Arab VARCHAR(100) COLLATE Arabic_100_CS_AS_WS_SC
,Rom VARCHAR(100) COLLATE Romanian_CI_AI);
INSERT INTO #TableVariable(deflt,Arab,Rom) VALUES('aBc','aBc','aBc');
SELECT CASE WHEN tv.deflt = 'ABC' THEN 'CI' ELSE 'CS' END AS CheckDefault
,CASE WHEN tv.Arab = 'ABC' THEN 'CI' ELSE 'CS' END AS CheckArab
,CASE WHEN tv.Rom = 'ABC' THEN 'CI' ELSE 'CS' END AS CheckRom
FROM #TableVariable AS tv
/*CI CS CI*/
UPDATE Some documentation
At this link You can read about the details. A collation does not change the value. It applys a rule (related to NOT NULL which does not change the values, but just adds the rule whether NULL can be set or not).
The documentation tells clearly
Is a clause that can be applied to a database definition or a column definition to define the collation, or to a character string expression to apply a collation cast.
And a bit later you'll find
Creating or altering a database
Creating or altering a table column
Casting the collation of an expression
UPDATE 2: A suggestion for a solution
If you want to have control whether a comparison is done CS or CI you might try this:
DECLARE #tbl TABLE(SomeValueInDefaultCollation VARCHAR(100));
INSERT INTO #tbl VALUES ('ABC'),('aBc');
DECLARE #CompareCaseSensitive BIT = 0;
DECLARE #SearchFor VARCHAR(100) = 'aBc';
SELECT *
FROM #tbl
WHERE (#CompareCaseSensitive=1 AND SomeValueInDefaultCollation=#SearchFor COLLATE Latin1_General_CS_AS)
OR (ISNULL(#CompareCaseSensitive,0)=0 AND SomeValueInDefaultCollation=#SearchFor COLLATE Latin1_General_CI_AS);
With #CompareCaseSensitive set to 1 it will return just the aBc, with NULL or 0 it will return both lines.
This is - for sure! - much better in performance than an UDF.
Please try using BINARY_CHECKSUM Function, and no need to UDF Function:
SELECT txt
FROM (SELECT 'ABC' AS txt UNION SELECT 'cdf') t
WHERE
BINARY_CHECKSUM(txt)= BINARY_CHECKSUM(UPPER(txt))
I think you are confused on how collation works. If you want to force a case sensitive collation you would do it in your where predicate, not with a function like that. And scalar functions are horrible for performance.
Here is how you would be able to use collation for this type of thing.
SELECT txt
FROM (SELECT 'ABC' AS txt UNION SELECT 'cdf') t
WHERE txt collate SQL_Latin1_General_CP1_CS_AS = UPPER(txt)
Here's what I did:
I changed the function to perform a comparison, instead of setting the collation, and then return a 1 or 0.
CREATE FUNCTION [dbo].[fnsConvert]
(
#p NVARCHAR(2000) ,
#c NVARCHAR(2000)
)
RETURNS BIT
AS
BEGIN
DECLARE #result BIT
IF ( #c = 'SQL_Latin1_General_CP1_CS_AS' )
BEGIN
IF #p COLLATE SQL_Latin1_General_CP1_CS_AS = UPPER(#p)
SET #result = 1
ELSE
SET #result = 0
END
ELSE
SET #result = 0
RETURN #result
END
Then the query that uses the function changes just a bit.
SELECT txt
FROM (SELECT 'ABC' AS txt UNION SELECT 'cdf') t
WHERE
dbo.fnsConvert(txt, 'SQL_Latin1_General_CP1_CS_AS') = 1
As #Shnugo stated, the collation is not an attribute of a variable, but it can be attribute of a column definition.
For collation-enabled comparison outside of TSQL, you can define a (persisted) computed column with an explicit collation:
create table Q47890189 (
txt nvarchar(100),
colltxt as txt collate SQL_Latin1_General_CP1_CS_AS persisted
)
insert into Q47890189 (txt) values ('ABC')
insert into Q47890189 (txt) values ('cdf')
select * from Q47890189 where txt = UPPER(txt)
select * from Q47890189 where colltxt = UPPER(colltxt)
Note that a persisted column can also be indexed, and has a better performance than calling a scalar function.
COLLATE :Is a clause that can be applied to a database definition or a column definition to define the collation, or to a character string expression to apply a collation cast.
COLLATE do not convert any column or variable..It define the characteristics of collate.
CREATE TABLE [dbo].[OINV]
[CardCode] [nvarchar](50) NULL
)
if i have a table with 5175460 rows
then converting this to another data type will take time because its of its value is converted to new data type.
alter table OINV
alter column CardCode varchar(50)
--1 min 45 sec
alter table OINV
alter column CardCode nvarchar(50) COLLATE SQL_Latin1_General_CP1_CS_AS
If i don't convert the data type and only want to change collate
then it take 1 ms to do so.That means it do not convert 5175460 rows to said collate.
It just define the collate on that column.
when this column is use in where condition then column will exhibit characteristics of said collate.
UDF/TVF is not perform-ant way to do so.Best way is to alter table
Another example,
declare #i varchar(60)='ABC'
SELECT txt
FROM (SELECT 'abc' AS txt UNION SELECT 'cdf') t
WHERE
txt = #i COLLATE SQL_Latin1_General_CP1_CS_AS
I can't declare it like this,
declare #i varchar(60) COLLATE SQL_Latin1_General_CP1_CS_AS='ABC'
So variable will exhibit collate characteristics only as long as it is use along collate .
In your case you are return only plain variable,
UDF way of doing so,
CREATE FUNCTION testfn (
#test VARCHAR(100)
,#i INT
)
RETURNS TABLE
AS
RETURN (
-- insert into #t values(#test)
SELECT #test COLLATE SQL_Latin1_General_CP1_CS_AS AS a
)
SELECT *
FROM (
SELECT 'ABC' AS txt
UNION
SELECT 'cdf'
) t
OUTER APPLY dbo.testfn(txt, 0) fn
WHERE fn.a = UPPER(txt)
To define multiple collate you have to define multiple table with different collate. TVF can return only static table schema,so there can be only one collate define.
Therefore TVF is not right way to perform your task.
I agree with #Shnugo when you create local variable it will take default collation
But, you could explicitly collate your variable values returned by function with your user defined collation as follow :
select * from
(SELECT 'ABC' AS txt UNION SELECT 'cdf') a
where (dbo.fnsConvert(txt, 'SQL_Latin1_General_CP1_CS_AS')
collate SQL_Latin1_General_CP1_CS_AS) = UPPER(txt)
In addition collate clause can only applied to database definition, column defination or string/character expression, in other words it is used for database objects i.e. tables, columns, indexes
collation_name can't be represented by variable or expression.
MSDN clearly defines COLLATE:
Is a clause that can be applied to a database definition or a column
definition to define the collation, or to a character string
expression to apply a collation cast.
Can you see a word about variable here?
If you need UDF, just use table-valued function:
CREATE FUNCTION dbo.test
(
#text nvarchar(max)
)
RETURNS TABLE
AS
RETURN
(
SELECT c COLLATE SQL_Latin1_General_CP1_CS_AS as txt
FROM (VALUES (#text)) as t(c)
)
GO
And use it like:
;WITH cte AS (
SELECT N'ABC' as txt
UNION
SELECT N'cdf'
)
SELECT c.txt
FROM cte c
OUTER APPLY dbo.test (c.txt) t
WHERE t.txt = UPPER(c.txt)
Output:
txt
------
ABC

Return Value Column Name

The stored procedure below works correctly as expected. Returning True if "FleetBarcode" exists and False if it doesn't.
However, when it returns it it displays it as below
(no column name)
True
My problem is I need the "No Column Name" part to have a defined column name. Tried the method below so far which gives the 'True' field an alias.
Thank you for your time.
ALTER proc [dbo].[usp_getjobs]
#barcode as nvarchar(20)
as
SELECT CASE WHEN EXISTS(
SELECT
[FleetBarcode],
[Deleted]
FROM w_DeliveryItems
WHERE FleetBarcode = #barcode
AND Deleted != 1
)
THEN (SELECT 'True' 'Exist')
ELSE (SELECT 'False' 'Exist')
END
Use
ALTER PROC [dbo].[usp_getjobs] #barcode AS NVARCHAR(20)
AS
SELECT CASE
WHEN EXISTS(SELECT [FleetBarcode],
[Deleted]
FROM w_DeliveryItems
WHERE FleetBarcode = #barcode
AND Deleted != 1) THEN 'True'
ELSE 'False'
END AS [Exist]
The alias needs to go on the outer SELECT.
Also for column aliasing it is more common to use square brackets than single quotes.
Get rid if the SELECTs in the THEN/ELSE blocks and use AS to give the column a name:
SELECT CASE WHEN EXISTS(
...
THEN 'True')
ELSE 'False')
END AS [Exist] --<-- add name here
ALTER proc [dbo].[usp_getjobs]
#barcode as nvarchar(20)
as
SELECT CASE WHEN EXISTS(
SELECT
[FleetBarcode],
[Deleted]
FROM w_DeliveryItems
WHERE FleetBarcode = #barcode
AND Deleted != 1
)
THEN (SELECT 'True' 'Exist')
ELSE (SELECT 'False' 'Exist')
END AS [Your Column Name]

Select with IN and Like

I have a very interesting problem. I have an SSRS report with a multiple select drop down.
The drop down allows to select more than one value, or all values.
All values is not the problem.
The problem is 1 or the combination of more than 1 option
When I select in the drop down 'AAA' it should return 3 values: 'AAA','AAA 1','AAA 2'
Right now is only returning 1 value.
QUESTION:
How can make the IN statement work like a LIKE?
The Drop down select
SELECT '(All)' AS team, '(All)' AS Descr
UNION ALL
SELECT 'AAA' , 'AAA'
UNION ALL
SELECT 'BBB' , 'BBB'
Table Mytable
ColumnA Varchar(5)
Values for ColumnA
'AAA'
'AAA 1'
'AAA 2'
'BBB'
'BBB 1'
'BBB 2'
SELECT * FROM Mytable
WHERE ColumnA IN (SELECT * FROM SplitListString(#Team, ',')))
Split function
CREATE FUNCTION [dbo].[SplitListString]
(#InputString NVARCHAR(max), #SplitChar CHAR(1))
RETURNS #ValuesList TABLE
(
param NVARCHAR(MAX)
)
AS
BEGIN
DECLARE #ListValue NVARCHAR(max)
DECLARE #TmpString NVARCHAR(max)
DECLARE #PosSeparator INT
DECLARE #EndValues BIT
SET #TmpString = LTRIM(RTRIM(#InputString));
SET #EndValues = 0
WHILE (#EndValues = 0) BEGIN
SET #PosSeparator = CHARINDEX(#SplitChar, #TmpString)
IF (#PosSeparator) > 1 BEGIN
SELECT #ListValue = LTRIM(RTRIM(SUBSTRING(#TmpString, 1, #PosSeparator -1 )))
END
ELSE BEGIN
SELECT #ListValue = LTRIM(RTRIM(#TmpString))
SET #EndValues = 1
END
IF LEN(#ListValue) > 0 BEGIN
INSERT INTO #ValuesList
SELECT #ListValue
END
SET #TmpString = LTRIM(RTRIM(SUBSTRING(#TmpString, #PosSeparator + 1, LEN(#TmpString) - #PosSeparator)))
END
RETURN
END
You can't. But, you can make the like work like the like:
select *
from mytable t join
SplitListString(#Team, ',') s
on t.ColumnA like '%'+s.param+'%'
That is, move the split list to an explicit join. Replace with the actual column name returned by the function, and use the like function.
Or, if you prefer:
select *
from mytable t cross join
SplitListString(#Team, ',') s
where t.ColumnA like '%'+s.param+'%'
The two versions are equivalent and should produce the same execution plan.
Better approach would be to have a TeamsTable (teamID, teamName, ...) and teamMembersTable (teamMemberID, teamID, teamMemberDetails, ...).
Then you an build your dropdown list as
SELECT ... FROM TeamsTable ...;
and
SELECT ... FROM teamMembersTable WHERE teamID IN (valueFromYourDropDown);
Or you can just store your teamID or teamName (or both) in your (equivalent of) teamMembersTable
You're not going to get IN to work the same as LIKE without a lot of work. You could do something like this though (and it would be nice to see some of your actual data though so we could give better solutions):
SELECT *
FROM table
WHERE LEFT(field,3) IN #Parameter
If you'd like better performance, create a code field on your table and update it like this:
UPDATE table
SET codeField = LEFT(field,3)
Then just add an index on that field and run this query to get your results:
SELECT *
FROM table
WHERE codeField IN #Parameter