Dynamic SQL for CASE Statements to remove hardcode - sql

I've a Dynamic SQL that is required to be optimized. I need to make CASE Expression Dynamic. I've a list of ATTRIBUTE_LIST & SCENARIO_LIST, that are provided below. I wrote a Function to get them Dynamically. How can I replace Three CASE Expression and make it Dynamic? I'm trying to avoid hard coding.
SET #ATTRIBUTE_LIST = 'symbol_type, currency, performing_status' -- INPUTS
SET #SCENARIO_LIST = 'historicalsimulation_1day_end_10dec2013, historicalsimulation_1day_end_11dec2013'
SELECT CASE
WHEN (GROUPING(Scenario_Name) = 1) THEN ''ALL''
WHEN (Scenario_Name = ''[BaseCase]'') THEN ''BaseCase''
ELSE ISNULL(Scenario_Name, '''')
END AS Scenario_Name,
CASE
WHEN (GROUPING(Symbol_Type) = 1) THEN ''ALL''
ELSE ISNULL(Symbol_Type, '''')
END AS Symbol_Type,
CASE
WHEN (GROUPING(Currency) = 1) THEN ''ALL''
ELSE ISNULL(Currency, '''')
END AS Currency,
CASE
WHEN (GROUPING(Performing_Status) = 1) THEN ''ALL''
ELSE ISNULL(Performing_Status, '''') \
END AS Performing_Status,
SUM(Value) AS ScenarioValue
FROM [20151005_171003_UserName_NT-22_Analysis_Tue] o
LEFT JOIN [20151005_171003_UserName_NT-22_Analysis_Tue_Position_Data] pld
ON o.Position_Unique_Identifier=pld.Position_Unique_Identifier
GROUP BY ' + #ATTRIBUTE_LIST + ' WITH CUBE) AS DATA
PIVOT ( Sum(scenariovalue)
FOR scenario_name IN (' + #SCENARIO_LIST + ')'

It appears you are using SQL Server. Here's a brute-force way of looping over your attributes and building a series of case expressions:
declare #cases nvarchar(max) = '';
declare #l varchar(max) = #ATTRIBUTE_LIST + ',';
declare #ofs int = 0;
declare #pos int = charindex(',', #l, #ofs + 1);
while #pos > 0
begin
if #cases <> '' set #cases = #cases + ',';
set #cases = #cases +
replace('
CASE WHEN (GROUPING(<COL>) = 1) THEN ''ALL''
ELSE ISNULL(<COL>, '''')
END AS <COL>',
'<COL>', substring(#l, #ofs + 1, #pos - #ofs - 1)
);
set #ofs = #pos;
set #pos = charindex(',', #l, #ofs + 1);
end
select #cases;

Related

How to fix all caps to normal case

I have a sql table that contains an employee's full name in all caps (i.e. SMITH-EASTMAN,JIM M).
I need to be able to separate the full name into two separate columns (Last Name and First Name). This part is going well. Now I could use some assistance with removing the capital letters and putting it in normal case.
How can I take the results of my common table expression and pass them into a function?
WITH CTE AS
(
SELECT FullName = [Employee Name],
LastName = SUBSTRING([Employee Name], 1, CHARINDEX(',',[Employee Name])-1),
FirstNameStartPos = CHARINDEX(',',[Employee Name]) + 1,
MidlleInitialOrFirstNameStartPos = CHARINDEX(' ',[Employee Name]),
MiddleInitialOrSecondFirstName = SUBSTRING([Employee Name], CHARINDEX(' ',[Employee Name]),LEN([Employee Name])),
MiddleInitialOrSecondFirstNameLen = LEN(SUBSTRING([Employee Name], CHARINDEX(' ',[Employee Name]),LEN([Employee Name]))) - 1
FROM ['Med-PS PCN Mapping$']
WHERE [PS Employee ID] IS NOT NULL
),
CTE2 AS
(
SELECT FullName = CTE.FullName,
DerivedFirstName = CASE
WHEN CTE.MiddleInitialOrSecondFirstNameLen = 1
THEN SUBSTRING(CTE.FullName, CTE.FirstNameStartPos, CTE.MidlleInitialOrFirstNameStartPos - CTE.FirstNameStartPos)
ELSE SUBSTRING(CTE.FullName, CTE.FirstNameStartPos, CTE.FirstNameStartPos + CTE.MiddleInitialOrSecondFirstNameLen)
END,
DerivedLastName = CTE.LastName
FROM CTE
)
SELECT *
FROM CTE2
RESULTS
FullName DerivedFirstName DerivedLastName
SMITH-EASTMAN,JIM M JIM SMITH-EASTMAN
O'DAY,MARTIN C MARTIN O'DAY
TROUT,MADISON MARIE MADISON MARI TROUT
CREATE FUNCTION [dbo].[FixCap] ( #InputString varchar(4000) )
RETURNS VARCHAR(4000)
AS
BEGIN
DECLARE #Index INT
DECLARE #Char CHAR(1)
DECLARE #PrevChar CHAR(1)
DECLARE #OutputString VARCHAR(255)
SET #OutputString = LOWER(#InputString)
SET #Index = 1
WHILE #Index <= LEN(#InputString)
BEGIN
SET #Char = SUBSTRING(#InputString, #Index, 1)
SET #PrevChar = CASE WHEN #Index = 1 THEN ' '
ELSE SUBSTRING(#InputString, #Index - 1, 1)
END
IF #PrevChar IN (' ', ';', ':', '!', '?', ',', '.', '_', '-', '/', '&', '''', '(')
BEGIN
IF #PrevChar != '''' OR UPPER(#Char) != 'S'
SET #OutputString = STUFF(#OutputString, #Index, 1, UPPER(#Char))
END
SET #Index = #Index + 1
END
RETURN #OutputString
END
GO
select [dbo].[FixCap] (pass in DerivedFirstName from CTE2);
select [dbo].[FixCap] (pass in DerivedLastName from CTE2);
Do you want something like INITCAP ?
CREATE FUNCTION dbo.F_INITCAP (#PHRASE NVARCHAR(max))
RETURNS NVARCHAR(max)
WITH RETURNS NULL ON NULL INPUT
AS
BEGIN
IF LEN(#PHRASE) < 1 RETURN #PHRASE;
DECLARE #I INT = 1, #C CHAR(1), #P BIT = 0, #OUT VARCHAR(max) = '';
WHILE #I <= LEN(#PHRASE)
BEGIN
SET #C = SUBSTRING(#PHRASE, #I, 1);
IF #C BETWEEN 'A' AND 'Z' COLLATE Latin1_General_CI_AI
BEGIN
IF #P = 0
SET #OUT = #OUT + UPPER(#C);
ELSE
SET #OUT = #OUT + LOWER(#C);
SET #P = 1
END
ELSE
BEGIN
SET #P = 0;
SET #OUT = #OUT + LOWER(#C);
END
SET #I = #I + 1;
END
RETURN #OUT;
END
GO

Can this dynamic SQL be replaced with the regular SQL to improve performance?

I am using the following dynamic query, but see that the performance is slow. I am not a big fan of dynamic SQL, and am looking for, if possible, a good clean and fast SQL alternative for the following. Thanks a million ton in advance! Here are some details:
In the following code, the final table missingfields_xxxx lists out the rows where we have a missing rule field.
table_name has the column rule that holds the column name of the table trans_modelname (this table can be found in the dynamic part of the sql)
DECLARE #rule NVARCHAR(MAX)
DECLARE #PeriodNumber INT = 1
DECLARE #SelectList NVARCHAR(MAX)
DECLARE #WhereList NVARCHAR(MAX)
DECLARE #SQL NVARCHAR(MAX)
DECLARE #ModelName as NVARCHAR(MAX) = 'modelname'
--DECLARE #MaxPeriods INT = 8
DECLARE #MaxPeriods INT
SELECT #MaxPeriods = count (*)
FROM
(
SELECT [rule]
FROM table_name
WHERE ModelName = #ModelName) ab
DECLARE db_cursor3 CURSOR FOR
SELECT * FROM
(
SELECT [rule]
FROM table_name
WHERE ModelName = #ModelName) cd
OPEN db_cursor3
FETCH NEXT FROM db_cursor3 INTO #rule
WHILE ##FETCH_STATUS = 0
BEGIN
BEGIN
SELECT #SelectList = COALESCE(#SelectList + ', ', '') + '' + #rule + ' AS [GLSegment_' + RIGHT('00' + CAST(#PeriodNumber AS VARCHAR), 3) + ']'
SELECT #SelectList as 'Selectlist'
IF #PeriodNumber < #MaxPeriods
BEGIN
SELECT #WhereList = COALESCE(#WhereList, '') + '(isnull([GLSegment_' + RIGHT('00' + CAST(#PeriodNumber AS VARCHAR), 3) + '],'''') = '''' ) OR '
SELECT #WhereList as 'Wherelist where periodnumber < maxperiods'
END
ELSE IF #PeriodNumber = #MaxPeriods
BEGIN
SELECT #WhereList = COALESCE(#WhereList, '') + '(isnull([GLSegment_' + RIGHT('00' + CAST(#PeriodNumber AS VARCHAR), 3) + '], '''') = '''' )'
SELECT #WhereList as 'Wherelist where periodnumber = maxperiods'
END
SET #PeriodNumber = #PeriodNumber + 1
END
FETCH NEXT FROM db_cursor3 INTO #rule
END
CLOSE db_cursor3
DEALLOCATE db_cursor3
-- build dynamic query
SET #SQL =
'SELECT * into missingfields_' + #ModelName + ' from trans_' + #ModelName + '
WHERE id in
(
SELECT id from
(
SELECT id, ' + #SelectList + '
FROM trans_' + #ModelName + ')A
WHERE ' + #WhereList + '
);
SELECT * from missingfields_' + #ModelName
PRINT #SQL
print 'missingfields_' + #ModelName
EXEC sp_executesql #SQL

Aliasing column names dynamically in SQL Server

Providing separate aliasing values to each column dynamically. The number of values in #column_name1 and #aliasing_val can change as per requirements, so please provide a solution that can work for any number of values in the #column_name1 and #aliasing_val.
Declare #table_name1 NVARCHAR(250) = 'table1',
#column_name1 NVARCHAR(250) = 't1col1,t1col2,t1col3',
#aliasing_val NVARCHAR(250) = 'c1,c2,c3',
#SQLString nvarchar(max),
#SQLString2 nvarchar(max)
If ((#table_name1 IS NOT NULL AND LEN(#table_name1) !=0)
AND (#column_name1 IS NOT NULL AND LEN(#column_name1) !=0))
BEGIN
set #SQLString2 = 'SELECT '
Select #SQLString2 = #SQLString2 +
QUOTENAME(split.a.value('.', 'VARCHAR(100)')) + ' As '+aliasing_val+',
'
FROM (SELECT Cast ('<M>' + Replace(#column_name1, ',', '</M><M>')+ '</M>' AS XML) AS Data) AS A
CROSS apply data.nodes ('/M') AS Split(a);
Set #SQLString2 = LEFT(#SQLString2, LEN(#SQLString2) - 3)
END
print #SQLString2
Current output:
SELECT [t1col1] As c1,c2,c3,
[t1col2] As c1,c2,c3,
[t1col3] As c1,c2,c3
Expected output
SELECT [t1col1] As c1,
[t1col2] As c2,
[t1col3] As c3
This should do the job. You need to separate the comma-separated elements of each string out one at a time. This script loops through the items and maintains a pointer to the next item in the list. Then at each loop it extracts the item and adds it to the SQL:
Declare #table_name1 NVARCHAR(250) = 'table1',
#column_name1 NVARCHAR(250) = 't1col1,t1col2,t1col3',
#aliasing_val NVARCHAR(250) = 'c1,c2,c3',
#SQLString nvarchar(max),
#SQLString2 nvarchar(MAX),
#count [int],
#pos int,
#pos2 [int],
#delimiter varchar(1);
SET #delimiter = ',';
SET #pos = CHARINDEX(#delimiter, #aliasing_val, 1)
SET #pos2 = CHARINDEX(#delimiter, #column_name1, 1)
SET #aliasing_val = LTRIM(RTRIM(#aliasing_val)) + #delimiter
SET #column_name1 = LTRIM(RTRIM(#column_name1)) + #delimiter
SET #count = 0
IF ((#table_name1 IS NOT NULL AND LEN(#table_name1) !=0) AND (#column_name1 IS NOT NULL AND LEN(#column_name1) !=0))
BEGIN
SET #SQLString2 = 'SELECT '
IF REPLACE(#aliasing_val, #delimiter, '') <> '' -- make sure there are actually any delimited items in the list
BEGIN
WHILE #pos > 0
BEGIN
IF #count > 0 SET #SQLString2 = #SQLString2 + ','
SET #SQLString2 = #SQLString2 + LTRIM(RTRIM(LEFT(#column_name1, #pos2 - 1))) + ' As ' + LTRIM(RTRIM(LEFT(#aliasing_val, #pos - 1)))
SET #aliasing_val = RIGHT(#aliasing_val, LEN(#aliasing_val) - #pos) -- remove the item we just extracted from the list
SET #column_name1 = RIGHT(#column_name1, LEN(#column_name1) - #pos2) -- remove the item we just extracted from the list
SET #pos = CHARINDEX(#delimiter, #aliasing_val, 1) -- reset the position to point to the next delimiter
SET #pos2 = CHARINDEX(#delimiter, #column_name1, 1) -- reset the position to point to the next delimiter
SET #count = #count + 1
END
END
END
SELECT #SQLString2
You are at the right approach. Only thing you need to do is make two separate select statement for #column_name and #aliasing_val and join it using ROW_NUMBER() function and you got what you expect - Try this
IF ((#table_name1 IS NOT NULL AND LEN(#table_name1) !=0) AND (#column_name1 IS NOT NULL AND LEN(#column_name1) !=0))
BEGIN
SET #SQLString2 = 'SELECT '
SELECT #SQLString2 += tab.ColName + ' As '+ res.AliasName +','
FROM
(
SELECT QUOTENAME(split.a.value('.', 'VARCHAR(100)')) AS ColName, ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS RowNum
FROM
(
SELECT Cast ('<M>' + Replace(#column_name1, ',', '</M><M>')+ '</M>' AS XML) AS Data
) AS A
CROSS apply data.nodes ('/M') AS Split(a)
) tab
INNER JOIN
(
Select AliasSplit.c.value('.', 'varchar(100)') AS AliasName, ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS RowNum
FROM
(
SELECT Cast ('<A>' + Replace(#aliasing_val, ',', '</A><A>')+ '</A>' AS XML) AS AliasData
) AS A
CROSS apply AliasData.nodes ('/A') AS AliasSplit(c)
) res
ON tab.RowNum = res.RowNum
Set #SQLString2 = LEFT(#SQLString2, LEN(#SQLString2) - 1)
END
PRINT #SQLString2
Output
SELECT [t1col1] As c1,[t1col2] As c2,[t1col3] As c3

Remove white spaces from string and convert into title case

Here is the example which i want in output...
I have this input = "Automatic email sent"
But I want this output = "AutomaticEmailSent"
Thanks In Advance!
Use TextInfo.ToTitleCase
// Defines the string with mixed casing.
string myString = "Automatic email sent";
// Creates a TextInfo based on the "en-US" culture.
TextInfo myTI = new CultureInfo("en-US",false).TextInfo;
// Changes a string to titlecase, then replace the spaces with empty
string outputString = myTI.ToTitleCase(myString).Replace(" ", "");
Stealing a function from this answer which takes an text input and make it proper case (otherwise known as title case):
create function ProperCase(#Text as varchar(8000))
returns varchar(8000)
as
begin
declare #Reset bit;
declare #Ret varchar(8000);
declare #i int;
declare #c char(1);
select #Reset = 1, #i=1, #Ret = '';
while (#i <= len(#Text))
select #c= substring(#Text,#i,1),
#Ret = #Ret + case when #Reset=1 then UPPER(#c) else LOWER(#c) end,
#Reset = case when #c like '[a-zA-Z]' then 0 else 1 end,
#i = #i +1
return #Ret
end
Then you can combine this function with REPLACE:
SELECT REPLACE(dbo.ProperCase(column), ' ', '')
FROM MyTable
SQL Server
declare #value varchar(64) = rtrim(' ' + 'Automatic email sent')
;with t(n) as (
select n = charindex(' ', #value, 0)
union all
select n = charindex(' ', #value, n + 1)
from t
where n > 0
)
select #value = stuff(#value, n + 1, 1, upper(substring(#value, n + 1, 1))) from t where n > 0
select replace(#value, ' ', '')

How to copy, replace, and insert across all rows in the database

I'll do my best to explain:
Copy all rows that exist in current database
Mass replace a specific string value in every row that contains a specific field
Insert the copied rows
Not sure what approach to take other than hammering out sql scripts.
Thanks!
Is this as simple as creating an Data Flow Source, selecting all the rows, then passing them to a Derived Column transformation, which would be along the lines of:
REPLACE( [ColumnName], "SpecificValue", "ReplacementValue" )
and then insert these rows into your destination table using the relevant Data Flow Destination.
I may be misunderstanding or simplifying step 2...
Assuming you have a table called "table2" and that table consists of the columns facilabbr, unitname, and sortnum... You can select all rows into a temporary table (# signifies a temporary table) changing the "unitname" column to something else...You'll be left with the new values in the temporary table. You can then replace the values in your initial table if you want.
INSERT INTO #temptable1
SELECT facilabbr,
'myNewUnitName' as unitname,
sortnum
FROM table2
DELETE FROM table2
INSERT INTO table2
SELECT facilabbr,
unitname,
sortnum
FROM #temptable1
--THIS QUERY IS ONLY EQUIPPED TO HANDLE:
--SIMPLE NUMERICS SUCH AS FLOATS, INTS, ETC
--SIMPLE STRING DATA TYPES LIMITED TO: VARCHARS, CHARS, NCHARS AND NVARCHARS
--DATES AND DATETIMES
Create Procedure SQLCloner
#TableName as VarChar(max), -- Table that holds data to clone.
#NewTableName as VarChar(max) = '', -- Table to Insert into. If same as Tablename leave blank or write ''.
#VarCharFind as VarChar(max) = '', -- Value to find (In order to replace). If you aren't replacing leave blank or write ''.
#VarCharReplace as VarChar(max) = '', -- Value to replace. If you aren't replacing leave blank or write ''.
#OptionalParam As VarChar(Max) = '' -- Your WHERE clause. If you have none leave blank or write ''.
AS
Declare #index as int = 1
Declare #rowcount As Int = 0
Declare #execFunction As VarChar(max) = ''
Declare #InsertTableRowName As VarChar(max) = ''
Declare #TempFilterType As VarChar(Max) = ''
--Create RowCount of Table
Select #ROWCOUNT = Count(*)
From (
Select Column_Name
From INFORMATION_SCHEMA.COLUMNS
Where Table_Name = '' + #TableName + ''
) As TheCount
--Use While Loop to create Table Columns
While #index <= #rowcount
Begin
--Determines the Variable type to change the exec function accordingly
Select #TempFilterType = TypeTable.DATA_TYPE
From (
Select Data_Type,
ROW_NUMBER() OVER (Order By Ordinal_Position) as RowNum
From INFORMATION_SCHEMA.COLUMNS
Where Table_Name = #TableName
) As TypeTable
Where TypeTable.RowNum = #index
--Prepares #InsertTableRowName With the first part of the string
Set #InsertTableRowName = Case
When #TempFilterType IN('varchar', 'nvarchar','char', 'nchar')
Then #InsertTableRowName + ''''''''' + '
When #TempFilterType IN('datetime', 'date')
Then #InsertTableRowName + ''''''''' + Convert(varchar(Max), '
Else
#InsertTableRowName + 'Convert(varchar(Max), '
End
--Determines the Name of the Column
Select #InsertTableRowName = #InsertTableRowName +
Case
When #TempFilterType IN('varchar', 'nvarchar','char', 'nchar')
Then 'ISNULL(' + 'Replace(' + Column_Name + ','''''''','''''''''''')' + ','''')'
When #TempFilterType IN('datetime', 'date')
Then 'ISNULL(' + 'Replace(' + Column_Name + ','''''''','''''''''''')' + ',''12/31/9999'')'
Else
'ISNULL(' + 'Replace(' + Column_Name + ','''''''','''''''''''')' + ',0)'
End
From (
Select Column_Name,
ROW_NUMBER() OVER (Order By Ordinal_Position) As RowNum
From INFORMATION_SCHEMA.COLUMNS
Where Table_Name = #TableName
) As TheRow
Where RowNum = #index
--Finishes Closes each column insert (in every instance)
Set #InsertTableRowName = Case
When #TempFilterType IN('varchar', 'nvarchar','char', 'nchar')
Then #InsertTableRowName + ' + '''''''''
When #TempFilterType IN('datetime', 'date')
Then #InsertTableRowName + ') + '''''''''
Else
#InsertTableRowName + ') '
End
--Links each Row together with commas and plus signs until the very end.
If #index < #rowcount
Begin
Set #InsertTableRowName = Case
When #TempFilterType IN('varchar', 'nvarchar','char', 'nchar')
Then #InsertTableRowName + ' + ' + ''',''' + ' + '
When #TempFilterType IN('datetime', 'date')
Then #InsertTableRowName + ' + '','' + '
Else
#InsertTableRowName + ' + '','' + '
End
End
Set #index = #index + 1
End
--Puts the Query together (without any of the Parameters yet).
--First, determine if a new table should be used instead.
If #NewTableName = ''
Begin
Set #NewTableName = #TableName
End
--Next, Build the Query, and do it accordingly with if there is a Find/Replace asked for.
Set #execFunction = 'Select '
If #VarCharFind <> ''
Begin
Set #execFunction = #execFunction + 'Replace('
End
Set #execFunction = #execFunction + '''insert into ' + #NewTableName + ' Values('' + ' + #InsertTableRowName + ' + '')'' '
If #VarCharFind <> ''
Begin
Set #execFunction = #execFunction + ', ''' + #VarCharFind + ''', ''' + #VarCharReplace + ''') '
End
Set #execFunction = #execFunction + 'From ' + #TableName
--Adds in the optional Parameters
If #OptionalParam <> ''
Begin
Set #execFunction = #execFunction + ' ' + #OptionalParam
End
Set #execFunction = #execFunction + CHAR(13)+CHAR(10)
--Executes the function and pulls an entire set of queries to copy into the new Database
Print #execFunction
Exec(#execFunction)
GO