SQL Server foreign characters equal empty string - sql

We have a query looking for empty values in a column, but it is returning results where the column is not empty.
As an example, this query returns true:
IF N'ការវិនិយោគបរទេស នៅជប៉ុន ធ្លាក់ចុះនៅឆ្នាំ២០១៧ នៅតែជាបញ្ហាធំបំផុត របស់ពិភពលោក' = N''
PRINT 'true'
ELSE
PRINT 'false';
I suspect it may be a collation issue, but I've tried forcing various collations and the result is still true.
IF N'ការវិនិយោគបរទេស នៅជប៉ុន ធ្លាក់ចុះនៅឆ្នាំ២០១៧ នៅតែជាបញ្ហាធំបំផុត របស់ពិភពលោក' = N'' COLLATE Arabic_CI_AI
PRINT 'true'
ELSE
PRINT 'false';

The text you want to compare seems to be Khmer, not Arabic, so you should use a Khmer_100_* collation.
Have a look at this TSQL snippet
declare #coll table (
name nvarchar(50),
khmerSupported bit
)
declare #text nvarchar(200) = N'ការវិនិយោគបរទេស នៅជប៉ុន ធ្លាក់ចុះនៅឆ្នាំ២០១៧ នៅតែជាបញ្ហាធំបំផុត របស់ពិភពលោក'
declare #name nvarchar(50), #sql nvarchar(500)
declare c cursor for
select name from sys.fn_helpcollations()
where name like 'khm%'
open c
fetch c into #name
while ##fetch_status = 0 begin
set #sql = 'select ''' + #name + ''', case when #text = N'''' COLLATE ' + #name + ' then 0 else 1 end'
print #sql
insert into #coll (name, khmerSupported)
exec sp_executesql #sql, N'#text NVARCHAR(200)', #text=#text
fetch c into #name
end
close c
deallocate c
select Name, KhmerSupported from #coll
and experiment with the collations query.
Use select Name, KhmerSupported from #coll where KhmerSupported=1 to find all collations supporting Khmer comparison.

Related

IF EXISTS does not return correct result

I have to check if a column in my sql table has null values and to print 'Yes'/'No'
DECLARE #columnName nvarchar(50)
SET #columnName = 'City'
IF EXISTS (SELECT 1 FROM rf.Country WHERE #columnName IS NULL)
BEGIN
PRINT 'Yes'
END
ELSE
BEGIN
PRINT 'No'
END
This query returns 'No' when I know for sure that there are null values in this column. I see that it is not allowed to use #var in WHERE statement but I need a way to loop through all columns in a table and to print result for each column if it contains NULL values.
You need to safely inject the value into a dynamic query, and then execute that:
DECLARE #ColumnName sysname; --synonym for nvarchar(128) NOT NULL
SET #ColumnName = N'City';
DECLARE #SQL nvarchar(MAX),
#CRLF nchar(2) = CHAR(13) + CHAR(10);
SET #SQL = N'IF EXISTS (SELECT 1 FROM rf.Country WHERE ' + QUOTENAME(#ColumnName) + N' IS NULL)' + #CRLF +
N' PRINT N''Yes'';' + #CRLF +
N'ELSE' + #CRLF +
N' PRINT N''No'';';
--PRINT #SQL; --Your debugging friend
EXEC sys.sp_executesql #SQL;
Your query is not checking the column values in the table. It is just checking the constant -- and #columnName IS NULL always evaluates to false because #columnName has a value.
You would need to use dynamic SQL. Perhaps:
DECLARE #columnName nvarchar(50)
SET #columnName = 'City';
SET #SQL = '
SELECT (CASE WHEN EXISTS (SELECT 1 FROM rf.Country WHERE #columnName IS NULL)
THEN 1 ELSE 0
END) as flag
';
SET #SQL = REPLACE(#SQL, '#columnName', #columnName)
DECLARE #flag INT;
EXEC sp_executeSQL #SQL,
N'#flag int OUTPUT',
#flag=#flag OUTPUT;
IF #flag = 1
PRINT 'Yes';
ELSE
PRINT 'No';
In general, I would discourage you from writing code where you need to pass column names around like this. That usually indicates a problem with the data model. I should say "usually". Under some circumstances, this can be convenient.

Remove quotation marks from all columns in SQL Server

I have a table with three columns: A, B and C.
A ; B; C
"a1"; "b1"; "c1"
"a2"; "b2"; "c3"
"a3"; "b3"; "c3"
I need to remove the quotation marks from all rows in the table. In this post I found a solution which works but requires to specify the names of all columns:
UPDATE myTable
SET A = REPLACE(A, '"', '');
UPDATE myTable
SET B = REPLACE(B, '"', '');
UPDATE myTable
SET C = REPLACE(C, '"', '');
QUESTION: Is there a less verbose way to apply the replace to all columns? For example a one-line expression?
Thanks
There isn't a one line script for this but I have a few lines in my code when I get rid of all the double quotes in the stagging table once I have got my data into sql server, mind you all of these columns are varchar data type.
-- Get rid of double quotes in the data
Declare #ColName SYSNAME , #Sql Nvarchar(Max)
Declare Cur Cursor FOR
SELECT c.name
from sys.columns c inner join sys.tables t on c.object_id = t.object_id
Where t.name = 'myTable' --<-- Your Table name
OPEN Cur
FETCH NEXT FROM Cur INTO #ColName
WHILE ##FETCH_STATUS = 0
BEGIN
SET #SQL = 'UPDATE myTable
SET ' + QUOTENAME(#ColName) + ' = LTRIM(RTRIM(ISNULL(REPLACE(' + QUOTENAME(#ColName) + ' , ''"'' , '''') , '''')))'
--PRINT #SQL
Exec sp_executesql #Sql
FETCH NEXT FROM Cur INTO #ColName
END
CLOSE Cur
DEALLOCATE Cur
GO
If number of records are not huge:-
You can script out the schema and data.
Replace the double quotes by Find and Replace All.
Run the cleaned script.
The below procedure will replace any single character with another single character in any table :)
USE [TSQL2012]--your database name
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE dbo.replace_char
#char_to_replace char(1), --character to be replaced
#expected_char char(1),
#table_name nvarchar(128) --your table name
AS
BEGIN
--Variable declaration
DECLARE #Column_Count_1 int
DECLARE #SQLString AS NVARCHAR(4000)
DECLARE #count int=1
DECLARE #column_name nvarchar(128)
--Getting the count of column
SET #SQLString=N'select #Column_Count=count(*) from '+ #table_name
EXEC sp_executesql #SQLString
, N'#Column_Count int OUTPUT'
, #Column_Count = #Column_Count_1 OUTPUT
--Getting the actual column names into a temporary table
select c.name as name
into #temp_column_names
from sys.tables t
join sys.columns c
on t.object_id=c.object_id
where t.name=#table_name
--Looping through each column to replace the required character
WHILE (#count<=#Column_Count_1)
BEGIN
--setting the column name
SET #column_name=(select top 1 name from
(select Row_number()over (order by name) as r_n_n, name
from #temp_column_names) aa
where r_n_n >=#count)
--updating the rows
SET #SQLString =N'Update ' + #table_name
SET #SQLString= #SQLString + N' Set ' + #column_name
SET #SQLString= #SQLString + N' = replace(' + #column_name
SET #SQLString =#SQLString + N',''' +#char_to_replace
SET #SQLString=#SQLString + N''',''' +#expected_char
SET #SQLString=#SQLString + N''');'
EXEC(#SQLString);
SET #count=#count+1;
END
--Dropping the temp table
DROP TABLE #temp_column_names
END
GO
Execution of the above procedure
EXEC dbo.replace_char #char_to_replace, #expected_char, #table_name
In your case
EXEC dbo.replace_char '"', '','Sample_1'
Sample_1 is the table which I created.

Dynamically selecting and grouping by specified parameters in SQL Server

I am trying to create optional parameters in a stored process in which I group by the parameters under certain conditions.
For example:
SELECT
TP.ProductID,
case
when #passangers='Y' then (TP.Passangersgroup)
when #fareclass='Y' then (TP.Fareclass)
when #ispriorbooking='Y' then (TP.IsPriorBooking)
end
INTO ##B
FROM ##A TP
GROUP BY
TP.ProductID,
case
when #passangers='Y' then (TP.Passangersgroup)
when #fareclass='Y' then (TP.Fareclass)
when #ispriorbooking='Y' then (TP.IsPriorBooking)
end
In this case, I would be able to select 'Y' for any of the 3 parameters, and I would want to add them to select statement and group by.
Any ideas?
You need to do this with dynamic SQL; something like:
declare #sql varchar(max) = 'SELECT
TP.ProductID, ' +
case when #passangers='Y' then 'TP.Passangersgroup'
when #fareclass='Y' then 'TP.Fareclass'
when #ispriorbooking='Y' then 'TP.IsPriorBooking'
else ''
end
+ ' INTO ##B
FROM ##A TP'
--ETC
Exec(#sql)
If you want to add up to all three columns, you need three case statements:
declare #sql varchar(max) = 'SELECT
TP.ProductID, ' +
case when #passangers='Y' then 'TP.Passangersgroup' else '' end
+ case when #fareclass='Y' then 'TP.Fareclass' else '' end
--ETC.
+ ' INTO ##B
FROM ##A TP'
Dynamic SQL will be the best bet, but I would figure out the column you want and then pass in the one column as a variable. Less likely to suffer SQL injection and more readable.
DECLARE #passangers CHAR(1), #fareclass CHAR(1), #ispriorbooking CHAR(1)
SET #passangers='Y'
DECLARE #SQLCMD NVARCHAR(MAX), #YValue NVARCHAR(1000)
--set the select and group by field
SELECT #YValue=
case
when #passangers='Y' then N'TP.Passangersgroup'
when #fareclass='Y' then N'TP.Fareclass'
when #ispriorbooking='Y' then N'TP.IsPriorBooking'
else NULL
end
IF #YValue IS NOT NULL
BEGIN
SET #SQLCMD=N'
SELECT
TP.ProductID,'+#YValue+'
INTO ##B
FROM ##A TP
GROUP BY
TP.ProductID, '+#YValue
PRINT #SQLCMD
--EXEC sp_executesql #SQLCMD
END
ELSE
PRINT 'INVALID PARAMETER PASSED IN'
You have to use dynamic sql but case statement mentioned in Steve Mangiameli code will not work in case when more than one column is selected as 'Y'. The below code will be working fine for multiple columns selected as 'Y'-
create procedure proc1
#passangers varchar(100) = null,
#fareclass varchar(100) = null,
#ispriorbooking varchar(100) = null
as
begin
declare #sql nvarchar(100)
declare #var varchar(100)
if #passangers = 'y'
set #var = tp.Passangersgroup + ', '
if #fareclass = 'y'
set #var = #var + TP.Fareclass + ', '
if #ispriorbooking = 'y'
set #var = #var + TP.IsPriorBooking
set #sql = 'select ' + #var + ' into ##b from ##a as TP group by ' + #var + 'option(recomplile)'
exec sp_executesql #sql
end

Find specific word in all rows in MS SQL database and eventually replace it

Is there a way to scan all tables in MS SQL 2008 R2 Database and replace one word to another? Or if replace is not possible maybe just possibility to list all rows with specific word (and corresponding table next to it for reference)?
If it's not possible to do purely in SQL then I can use C# as well.
There is no "out of the box" solution for this, but it's not very hard to write a stored procedure that does this.
For example, the procedure below will loop over all the tables and then loop over all the columns of type varchar and nvarchar and replace the string #value with #newvalue. This is just a proof of concept and can be enhanced greatly to make it faster by adding a where clause that checks if the string contains the value for example. (with LIKE or using full text indexes).
create proc ReplaceStrings(
#value nvarchar(maX)
, #newvalue nvarchar(max)
)
AS
declare #table_id int
, #name sysname
, #fieldname sysname
, #sql nvarchar(max)
, #fields nvarchar(max)
if #value = ''
begin
raiserror('The search value can not be empty', 16, 1)
return (-1)
end
declare tab cursor read_only local
for
select object_id, name from sys.tables
open tab
fetch next from tab into #table_id, #name
while ##FETCH_STATUS = 0
begin
SELECT #sql = N'UPDATE ' + QUOTENAME(#name) + '
set '
, #fields = NULL
declare field cursor read_only local
for
select name from sys.columns where object_id = #table_id and system_type_id in (type_id('varchar'), type_id('nvarchar'))
open field
fetch next from field into #fieldname
while ##FETCH_STATUS = 0
begin
set #fields = coalesce(#fields + ',', '') + N' ' + quotename(#fieldname) + ' = REPLACE(' + quotename(#fieldname) + ', #value, #newvalue)'
fetch next from field into #fieldname
end
close field
deallocate field
set #sql += #fields
print #sql
exec sp_executesql #sql , N'#value nvarchar(max), #newvalue nvarchar(max)', #value, #newvalue
fetch next from tab into #table_id, #name
end
close tab
deallocate tab
return (0)
Call the procedure like this:
exec ReplaceStrings 'haha', 'hihi'

SQL query shorthand for not selecting null columns when doing select all

when I do:
SELECT *
FROM SOMETABLE
I get all the columns from SOMETABLE, but I DON'T want the columns which are NULL (for all records). How do I do this?
Reason: this table has 20 columns, 10 of these are set but 10 of them are null for certain queries. And it is time consuming to type the columnnames....
Thanks,
Voodoo
SQL supports the * wildcard which means all columns. There is no wildcard for all columns except the ones you don't want.
Type out the column names. It can't be more work than asking questions on Stack Overflow. Also, copy & paste is your friend.
Another suggestion is to define a view that selects the columns you want, and then subsequently you can select * from the view any time you want.
It's possible to do, but kind of complicated. You can retrieve the list of columns in a table from INFORMATION_SCHEMA.COLUMNS. For each column, you can run a query to see if any non-null row exists. Finally, you can run a query based on the resulting column list.
Here's one way to do that, with a cursor:
declare #table_name varchar(256)
set #table_name = 'Airports'
declare #rc int
declare #query nvarchar(max)
declare #column_list varchar(256)
declare columns cursor local for select column_name
from INFORMATION_SCHEMA.COLUMNS where TABLE_NAME = #table_name
open columns
declare #column_name varchar(256)
fetch next from columns into #column_name
while ##FETCH_STATUS = 0
begin
set #query = 'select #rc = count(*) from ' + #table_name + ' where ' +
#column_name + ' is not null'
exec sp_executesql #query = #query, #params = N'#rc int output',
#rc = #rc output
if #rc > 0
set #column_list = case when #column_list is null then '' else
#column_list + ', ' end + #column_name
fetch next from columns into #column_name
end
close columns
deallocate columns
set #query = 'select ' + #column_list + ' from ' + #table_name
exec sp_executesql #query = #query
This runs on SQL Server. It might be close enough for Sybase. Hopefully, this demonstrates that typing out a column list isn't that bad :-)