Find the column(s) with values which has special characters using SQL - sql

I have some tables in a DB as mentioned below.,
**Table1**
+-------+--------------+-------------+
|**ID** |**Name** |**Country** |
+-------+--------------+-------------+
| 1 |Avinash Kumar |India |
| 2 |Sat!sh#S |USA |
| 3 |$32kjs |UK |
| 4 |#$### |AFRICA |
| 5 |Krishnas_has |USA |
+-------+--------------+-------------+
**Table2**
+-------+--------------+-------------+-----------+
|**ID** |**Name1** |**Country1** |**Region1**|
+-------+--------------+-------------+-----------+
| 1 |Avinash Kumar |India |EMEA |
| 2 |Sat!sh#S |USA |ASIA## |
| 3 |$32kjs |UK |EU._A |
| 4 |#$### |AFRICA |HAS&# |
| 5 |Krishnas_has |USA |KALc!! |
+-------+--------------+-------------+-----------+
**Table3**
+-------+--------------+-------------+-----------+-----------+
|**ID** |**Name2** |**Country2** |**Region2**|**State2** |
+-------+--------------+-------------+-----------+-----------+
| 1 |Avinash.Kumar |India$ |EMEA |BANG_& |
| 2 |Sat!sh#S |US!!A |ASIA## |SO$TH |
| 3 |$32kjs |U#K |EU._A |TRUTH |
| 4 |#$### |AFRICA |HAS |HAPPY |
| 5 |Krishnas_has |USA# |KALc!! |!ASDF# |
+-------+--------------+-------------+-----------+-----------+
And one more thing is like I want to pass the table name as variable. Something like this
Declare #Table
Select * from #Table
What ever I pass in #Table the code has to work dynamically
Now how can I find the special character columns if i pass #Table = Table3
For example: My output should be like this when I want to find special characters in Table3
**EXPECTED OUTPUT**
+-------+--------------+-------------+-----------+-----------+
|**ID** |**Name2** |**Country2** |**Region2**|**State2** |
+-------+--------------+-------------+-----------+-----------+
| 1 |Avinash.Kumar |India$ | |BANG_& |
| 2 |Sat!sh#S |US!!A |ASIA## |SO$TH |
| 3 |$32kjs |U#K |EU._A | |
| 4 |#$### | | | |
| 5 |Krishnas_has |USA# |KALc!! |!ASDF# |
+-------+--------------+-------------+-----------+-----------+
Can someone please help me with this?
Thanks in advance!

Have a look at this approach:
CREATE TABLE Test (
ID INT,
Name2 NVARCHAR(100),
Country2 NVARCHAR(100),
Region2 NVARCHAR(100),
State2 NVARCHAR(100)
)
GO
DELETE test
INSERT INTO Test VALUES(1, 'Avinash.Kumar', 'India$', 'EMEA', 'BANG_&'), (2, 'Sat!sh#S', 'US!!A', 'ASIA', 'SO$TH'), (3, 'Test1', 'Test2', 'Test3', 'Test4')
GO
CREATE FUNCTION chkStrg4Chars(#InputString NVARCHAR(4000)) RETURNS NVARCHAR(4000)
AS
BEGIN
DECLARE #RetVal NVARCHAR(4000);
SET #RetVal = CASE WHEN #InputString LIKE '%[^A-Za-z0-9 .]%' THEN #InputString ELSE NULL END;
RETURN #RetVal
END;
GO
CREATE PROCEDURE chkTableColumns4Chars(#InputTable NVARCHAR(500))
AS
BEGIN
DECLARE #ColList NVARCHAR(MAX);
DECLARE #ColListNonC NVARCHAR(MAX);
DECLARE #ColListRes NVARCHAR(MAX);
DECLARE #ColListPiv NVARCHAR(MAX);
SELECT #ColList = (SELECT CASE WHEN t.name IN ('nvarchar','varchar') THEN 'dbo.chkStrg4Chars(' + c.name + ') AS ' + c.name ELSE c.name END + ','
FROM sys.columns c
JOIN sys.types t ON c.system_type_id = t.system_type_id AND c.user_type_id = t.user_type_id
WHERE c.object_id = OBJECT_ID(#InputTable)
ORDER BY c.column_id
FOR XML PATH('')
);
SET #ColList = SUBSTRING(#ColList, 1, LEN(#ColList)-1);
SELECT #ColListRes = (SELECT 'MAX(' + c.name + ') AS ' + c.name + ','
FROM sys.columns c
JOIN sys.types t ON c.system_type_id = t.system_type_id AND c.user_type_id = t.user_type_id
WHERE c.object_id = OBJECT_ID(#InputTable)
AND t.name IN ('nvarchar','varchar')
ORDER BY c.column_id
FOR XML PATH('')
);
SET #ColListRes = SUBSTRING(#ColListRes, 1, LEN(#ColListRes)-1);
SELECT #ColListNonC = (SELECT c.name + ','
FROM sys.columns c
JOIN sys.types t ON c.system_type_id = t.system_type_id AND c.user_type_id = t.user_type_id
WHERE c.object_id = OBJECT_ID(#InputTable)
AND t.name NOT IN ('nvarchar','varchar')
ORDER BY c.column_id
FOR XML PATH('')
);
SELECT #ColListPiv = (SELECT c.name + ','
FROM sys.columns c
JOIN sys.types t ON c.system_type_id = t.system_type_id AND c.user_type_id = t.user_type_id
WHERE c.object_id = OBJECT_ID(#InputTable)
AND t.name IN ('nvarchar','varchar')
ORDER BY c.column_id
FOR XML PATH('')
);
SET #ColListPiv = SUBSTRING(#ColListPiv, 1, LEN(#ColListPiv)-1);
DECLARE #stmt NVARCHAR(MAX) = 'SELECT ' + #ColList + ' INTO #TTest1 FROM ' + #InputTable + ';
DECLARE #SubCol nvarchar(max) = (SELECT ColName + ' + CHAR(39) + ',' + CHAR(39) + ' FROM (SELECT ' + #ColListRes + ' FROM #TTest1) x UNPIVOT (StrVal FOR ColName in (' + #ColListPiv + '))u FOR XML PATH('+CHAR(39)+CHAR(39)+'));
SET #SubCol = SUBSTRING(#SubCol, 1, len(#SubCol)-1)
DECLARE #SubStmt nvarchar(max) = ' + CHAR(39) + 'SELECT + ' + #ColListNonC + char(39) + ' + #SubCol + ' + CHAR(39) + ' FROM #TTest1 WHERE COALESCE( ' + CHAR(39) + ' + #SubCol + '+ CHAR(39) + ') IS NOT NULL' + CHAR(39) + ';
EXEC sp_executesql #SubStmt;'
EXEC sp_executesql #stmt;
END
GO
EXEC chkTableColumns4Chars 'dbo.Test'

As you said if you have lot of columns in your table, then writing the sql query to check each column would be a hectic work. For easiness we can do it by executing dynamic sql query.
Query
declare #sql as varchar(max);
select #sql = stuff((
select ', case when [' + [column_name] + '] like '
+ char(39) + '%[^A-Za-z0-9 ]%' + char(39)
+ ' then [' + [column_name] + '] else '
+ char(39) + char(39) + ' end as [' + [column_name] + '] '
from information_schema.columns
where table_name = 'your_table_name'
and [column_name] <> 'ID'
for xml path('')
)
, 1, 1, ''
);
select #sql = 'select [ID], ' + #sql + ' from [your_table_name];';
exec(#sql);

Related

How to unpivot table with dynamic columns

I'm trying to transpose a table called CHECK_CARD. It looks like this:
_________________________________________________________________
| anc_report_date | RiskSignal | group_company_attr |
|_____________________________|______________|____________________|
| 2019-01-01 00:00:00.0000000 | NoRiskSignal | 4894543 |
| 2016-07-01 00:00:00.0000000 | RiskSignal | 1242151 |
I want it to look like this:
____________________________________________________________________________
| anc_report_date |2019-01-01 00:00:00.0000000|2016-07-01 00:00:00.0000000|
|____________________|___________________________|___________________________|
| RiskSignal | NoRiskSignal | RiskSignal |
| group_company_attr | 1242151 | 1242151 |
The number of rows in anc_report_date is dynamic so what I'm trying to do is:
DECLARE #Column NVARCHAR(MAX)
SET #Column
= 'SELECT cc.anc_report_date
from CHECK_CARD cc
inner join CHECK_CARD chc on cc.group_company_attr=chc.group_company_attr and cc.anc_report_date<=chc.anc_report_date and chc.id=1832307
where cc.status=1'
exec sp_executesql #Column
Here I collect all column names.
DECLARE #Column NVARCHAR(MAX)
SET #Column
= 'SELECT cc.anc_report_date
from CHECK_CARD cc
inner join CHECK_CARD chc on cc.group_company_attr=chc.group_company_attr and cc.anc_report_date<=chc.anc_report_date and chc.id=1832307
where cc.status=1'
DECLARE #Transpose as NVARCHAR(MAX)
set #Transpose
= 'SELECT RiskSignal, group_company_attr
from CHECK_CARD
unpivot
(
kek
for anc_report_date in ('+ #Column +')
) u'
exec sp_executesql #Transpose;
But when I use it all together it returns "Incorrect syntax near the keyword 'SELECT'."
I think this line ('+ #Columns +') is the reason.
Any ideas?
PRINT #Transpose;
[2019-09-19 18:54:42] [S0001] SELECT RiskSignal, group_company_attr
[2019-09-19 18:54:42] from CHECK_CARD
[2019-09-19 18:54:42] unpivot
[2019-09-19 18:54:42] (
[2019-09-19 18:54:42] kek
[2019-09-19 18:54:42] for anc_report_date in (SELECT cc.anc_report_date
[2019-09-19 18:54:42] from CHECK_CARD cc
[2019-09-19 18:54:42] inner join CHECK_CARD chc on cc.group_company_attr=chc.group_company_attr and cc.anc_report_date<=chc.anc_report_date and chc.id=1832307 where cc.status=1)
[2019-09-19 18:54:42] ) u
[2019-09-19 18:54:42] completed in 3 ms
Made it, Thanks all!
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(cc.id)
from CHECK_CARD cc
inner join CHECK_CARD chc
on cc.group_company_attr = chc.group_company_attr and cc.anc_report_date <= chc.anc_report_date and
chc.id = 1832307
where cc.status = 1
group by cc.id
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = N'SELECT AttrName,' + #cols + N' from
(
select id, AttrName, value
from CHECK_CARD
cross apply
(
select ''RiskSignal'', cast(RiskSignalas varchar(50)) union all
select ''group_company_attr'', cast(group_company_attr varchar(50))
) c(AttrName, value)
) d
pivot
(
max(value)
for id in (' + #cols + N')
) piv';
exec sp_executesql #query;

Dynamically select distinct current and previous columns from a sql table

I have a temporary table like so
Id |Name |Status | Rate | Method |ModifiedTime |ModifiedBy
-----------------------------------------------------------------------------
1 |Recipe1 | 0 | 30 | xyz | 2016-07-26 14:55:57.977 | A
-------------------------------------------------------------------------------
2 |Recipe1 | 0 | 30 | abc | 2016-07-26 14:56:18.123 | A
--------------------------------------------------------------------------------
3 |Recipe1 | 1 | 30 | xyz | 2016-07-26 14:57:50.180 | b
I would like to select only the changes and wanted to show what the value was previously and what it is currently accompanied by who changed it. The final outcome will be as follows. I am using SQL Server 2014.
Item | Before | After |ModifiedTime | ModifiedBy
-----------------------------------------------------------------------------
Method | xyz | Abc | 2016-07-26 14:56:18.123 | A
-------------------------------------------------------------------------------
Status | 0 | 1 | 2016-07-26 14:57:50.180 | b
--------------------------------------------------------------------------------
Method | Abc | xyz | 2016-07-26 14:57:50.180 | b
I would like to do this dynamically instead of specifying each column name individually as shown in this link
Link
Assuming NAME (Recipe1) is a key
Declare #Table table (Id int,Name varchar(50),Status int,Rate int,Method varchar(50),ModifiedTime DateTime,ModifiedBy varchar(50))
Insert Into #Table values
(1,'Recipe1',0,30,'xyz','2016-07-26 14:55:57.977','A'),
(2,'Recipe1',0,30,'abc','2016-07-26 14:56:18.123','A'),
(3,'Recipe1',1,30,'xyz','2016-07-26 14:57:50.180','b')
Declare #XML xml
Set #XML = (Select * from #Table for XML RAW)
;with cteBase as (
Select ID = r.value('#Id','int')
,Name = r.value('#Name','varchar(150)')
,ModifiedTime = r.value('#ModifiedTime','varchar(150)')
,ModifiedBy = r.value('#ModifiedBy','varchar(150)')
,Item = Attr.value('local-name(.)','varchar(max)')
,Value = Attr.value('.','varchar(max)')
From #XML.nodes('/row') AS A(r)
Cross Apply A.r.nodes('./#*[local-name(.)!="Id"]') AS B(Attr)
)
,cteExt as (Select *,LastValue =Lag(Value) over (Partition By Name,Item Order by ModifiedTime) From cteBase)
Select Name
,Item
,Before=LastValue
,After =Value
,ModifiedTime
,ModifiedBy
From cteExt
Where Value<>LastValue and LastValue is not null
and Item not in ('ModifiedTime','ModifiedBy')
Order By Name,ModifiedTime
Returns
Name Item Before After ModifiedTime ModifiedBy
Recipe1 Method xyz abc 2016-07-26T14:56:18.123 A
Recipe1 Method abc xyz 2016-07-26T14:57:50.180 b
Recipe1 Status 0 1 2016-07-26T14:57:50.180 b
Ok, I adapted my previous answer but on Dynamic SQL. It's a little crazy but it works (using testTable as table name you can change that just replace 'testTable'):
DECLARE #query NVARCHAR(max)
SET #query = 'select item, case item';
SELECT #query = #query + Stuff(( SELECT
' when '''+a.NAME+''' then cast(prev'+a.NAME+' as varchar) '
FROM sys.all_columns a JOIN sys.tables t ON
t.object_id = a.object_id AND t.NAME = 'testTable' AND a.NAME
NOT IN ('id',
'Name', 'ModifiedTime', 'ModifiedBy') FOR xml path('') ), 1, 0,
'');
SET #query = #query + ' end as Before, case item ';
SELECT #query = #query + Stuff(( SELECT
' when '''+a.NAME+''' then cast('+a.NAME+' as varchar) '
FROM sys.all_columns a JOIN sys.tables t ON t.object_id =
a.object_id AND t.NAME = 'testTable' AND a.NAME NOT IN (
'id',
'Name',
'ModifiedTime', 'ModifiedBy') FOR xml path('') ), 1, 1,
'');
SET #query = #query
+ ' end as After, ModifiedTime, ModifiedBy from ( select ';
SELECT #query = #query + Stuff(( SELECT a.NAME +', lag('+ a.NAME +
') over (partition by Name order by id) prev'+a.NAME+', '
FROM
sys.all_columns a JOIN sys.tables t ON t.object_id =
a.object_id
AND t.NAME = 'testTable' AND a.NAME NOT IN ('id', 'Name',
'ModifiedTime', 'ModifiedBy') FOR xml path('') ), 1, 0,
'');
SET #query = #query
+ ' ModifiedBy, ModifiedTime from testTable ) as t1 cross join ('
;
SELECT #query = #query + Stuff(( SELECT ' select '''+ a.NAME +
'''as item union all '
FROM
sys.all_columns a JOIN sys.tables t ON t.object_id =
a.object_id
AND t.NAME =
'testTable' AND a.NAME NOT IN ('id', 'Name',
'ModifiedTime',
'ModifiedBy') FOR xml path('') ), 1, 1, '');
SET #query = LEFT(#query, Len(#query) - 10); --get rid of last union all
SET #query = #query + ' ) items where ';
SELECT #query = #query + Stuff(( SELECT ' or (item = '''+ a.NAME +''' and '+
a.NAME +
' != prev'+ a.NAME +')' FROM sys.all_columns a JOIN
sys.tables
t ON t.object_id = a.object_id AND t.NAME = 'testTable'
AND
a.NAME NOT IN ('id', 'Name', 'ModifiedTime', 'ModifiedBy'
) FOR
xml
path('') ), 1, 3, '');
SET #query = #query + ' order by ModifiedTime';
EXECUTE Sp_executesql
#query

T-SQL PIVOT on one column to append the others

Given this table (there's always 2 duplicates of Combinations):
Combination Variable Value
-----------------------------
0 a 1
0 b 2
1 c 3
1 d 4
2 e 5
2 f 6
...
I want to query it to get this:
Variable 0 Value 0 Variable 1 Value 1 Variable 2 Value 2 ...
---------------------------------------------------------------------------
a 1 c 3 e 5
b 2 d 4 f 6
I've tried using PIVOT with dynamic query but cannot come up with a satisfying result.
Can somebody please advise?
EDIT: though Ullas solution works perfectly for combination pairs, I was wondering if it's possible to achieve the same result with combination N-uplets (e.g. (0, 0, 0), (1, 1, 1), (2, 2, 2) should result in 3 rows)? I reckon dynamic query is still the way to go, maybe with PIVOT this time.
Use dynamic sql.
I just created one. Don't know how efficient it is.
Query
declare #query1 varchar(max);
declare #query2 varchar(max);
select #query1 = 'select ' +
STUFF
(
(
select distinct
',min(t.Variable' + cast(Combination as varchar(6)) + ') as Variable' +
cast(Combination as varchar(6)) +
',min(t.Value' + cast(Combination as varchar(6)) + ') as Value' +
cast(Combination as varchar(6))
from tblComb
for xml path('')
),
1,1,'');
select #query1 += ' from('
select #query1 += 'select '+
stuff
(
(
select distinct
',max(case when Combination = ' + cast(Combination as varchar(6)) +'
then Variable end) as Variable' + cast(Combination as varchar(6)) +
',max(case when Combination = ' + cast(Combination as varchar(6)) +'
then Value end) as Value' + cast(Combination as varchar(6))
from tblComb
for xml path('')
),
1, 1, '');
select #query1 += ' from tblComb group by Combination, Variable)t union all ';
select #query2 = 'select ' +
STUFF
(
(
select distinct
',max(t.Variable' + cast(Combination as varchar(6)) + ') as Variable' +
cast(Combination as varchar(6)) +
',max(t.Value' + cast(Combination as varchar(6)) + ') as Value' +
cast(Combination as varchar(6))
from tblComb
for xml path('')
),
1, 1, '');
select #query2 += ' from('
select #query2 += 'select '+
stuff
(
(
select distinct
',max(case when Combination = ' + cast(Combination as varchar(6)) +'
then Variable end) as Variable' + cast(Combination as varchar(6)) +
',max(case when Combination = ' + cast(Combination as varchar(6)) +'
then Value end) as Value' + cast(Combination as varchar(6))
from tblComb
for xml path('')
),
1, 1, '');
select #query2 += ' from tblComb group by Combination, Variable)t;';
select #query1 += #query2;
execute(#query1);
Sample table
+-------------+----------+-------+
| Combination | Variable | Value |
+-------------+----------+-------+
| 0 | a | 1 |
| 0 | b | 2 |
| 1 | c | 3 |
| 1 | d | 4 |
| 2 | e | 5 |
| 2 | f | 6 |
+-------------+----------+-------+
Result set
+-----------+--------+-----------+--------+-----------+--------+
| Variable0 | Value0 | Variable1 | Value1 | Variable2 | Value2 |
+-----------+--------+-----------+--------+-----------+--------+
| a | 1 | c | 3 | e | 5 |
| b | 2 | d | 4 | f | 6 |
+-----------+--------+-----------+--------+-----------+--------+

How to pivot the given structure to expected one?

I have queries results as in the image, similarly for 90 date's, so how to group object and make date's as columns and count to respective date's.
Thanks in advance!!
You can do it using a dynamic crosstab:
SQL Fiddle
DECLARE #sql1 VARCHAR(4000) = ''
DECLARE #sql2 VARCHAR(4000) = ''
DECLARE #sql3 VARCHAR(4000) = ''
SELECT #sql1 =
'SELECT
[Object]' + CHAR(10)
SELECT #sql2 = #sql2 +
' , MAX(CASE WHEN [Date] = CAST(''' + CONVERT(VARCHAR(8), [Date], 112) + ''' AS DATE) THEN [count] END) AS ' + QUOTENAME([Date]) + CHAR(10)
FROM(
SELECT DISTINCT [Date] FROM tbl
)t
ORDER BY [Date]
SELECT #sql3 =
'FROM tbl
GROUP BY [Object]
ORDER BY [Object]'
PRINT(#sql1 + #sql2 + #sql3)
EXEC (#sql1 + #sql2 + #sql3)
RESULT
| Object | 2015-01-01 | 2015-01-02 |
|--------|------------|------------|
| 1 | 10 | 34 |
| 2 | 20 | 46 |
| 3 | 130 | 78 |
| 4 | 40 | 89 |
| 5 | 55 | 45 |
This is the output of the PRINT command:
SELECT
[Object]
, MAX(CASE WHEN [Date] = CAST('20150101' AS DATE) THEN [count] END) AS [2015-01-01]
, MAX(CASE WHEN [Date] = CAST('20150102' AS DATE) THEN [count] END) AS [2015-01-02]
FROM tbl
GROUP BY [Object]
ORDER BY [Object]
You can use SQL Server PIVOT relational operator
DECLARE #SQLQuery AS NVARCHAR(MAX)
DECLARE #PivotColumns AS NVARCHAR(MAX)
--Get unique values of pivot column
SELECT #PivotColumns= COALESCE(#PivotColumns + ',','') + QUOTENAME([Date])
FROM (SELECT DISTINCT [Date] FROM [dbo].[PivotExample]) AS PivotExample
--Create the dynamic query with all the values for
--pivot column at runtime
SET #SQLQuery =
N'SELECT ObjectId, ' + #PivotColumns + '
FROM [dbo].[PivotExample]
PIVOT( SUM(COUNT)
FOR [Date] IN (' + #PivotColumns + ')) AS P'
EXEC sp_executesql #SQLQuery

How to pivot table rows into columns with dynamic name

I'm having a very tough time trying to figure out how to do
CID sUSER VALUE
------------------
001 235 10
001 188 20
001 04 5
002 235 11
002 188 12
002 04 13
I would like it displayed as follows
CID 04 188 235
-------------------
001 5 20 10
002 13 12 11
Can someone please show me the Sql code please ?
Lets Assume you have filtered data in #tmpCID; now try below script
Declare #sUser As Varchar(Max)
SELECT #sUser= Case isNull(#sUser,'')
When '' then '['+ Rtrim(sUser) + ']'
Else COALESCE(#sUser + ',[', ',') + Rtrim(sUser)+ ']'
End
FROM (SELECT DISTINCT sUser FROM #tmpCID) U
Declare #Query As Varchar(Max)
Set #Query='Select CID, ' + #sUser + ' From #tmpCID
Pivot (AVG(Value)
For sUser IN ('+ #sUser + ')) As P'
EXEC (#Query)
Try this:
CREATE PROCEDURE YourProcedureName
as
Begin
DECLARE #sUSER AS NVARCHAR(MAX);
DECLARE #query AS NVARCHAR(MAX);
select #sUSER = STUFF((SELECT distinct ',' + QUOTENAME(sUSER) FROM testPIVOT
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '');
SELECT #query = 'WITH PivotData AS
(
SELECT CID, sUSER, VALUE
FROM dbo.YourTableName
)
SELECT CID, '+ #sUSER +'
FROM PivotData
PIVOT(max(VALUE) FOR sUSER IN('+ #sUSER +')) AS P;';
execute(#query);
End
Hope it could help you!
Thanks.