TSQL - Convert Money to Spanish Text - tsql

I have come across a very cool function from another source that translates money data types to English Text. It's great, but I also need to do this in Spanish. I tried to edit the numbers to Spanish words but of course the "rules" of Spanish numbering aren't the same as English. Does anyone have something already for converting a money data type to a Spanish text? Below is the one for English for reference. Or can a Spanish speaking db dev help me out here with modifying this one?
CREATE FUNCTION [dbo].[fnNumberToEnglish](#Money AS money)
RETURNS VARCHAR(1024)
AS
BEGIN
DECLARE #Number as BIGINT
SET #Number = FLOOR(#Money)
DECLARE #Below20 TABLE (ID int identity(0,1), Word varchar(32))
DECLARE #Below100 TABLE (ID int identity(2,1), Word varchar(32))
INSERT #Below20 (Word) VALUES
( 'Zero'), ('One'),( 'Two' ), ( 'Three'),
( 'Four' ), ( 'Five' ), ( 'Six' ), ( 'Seven' ),
( 'Eight'), ( 'Nine'), ( 'Ten'), ( 'Eleven' ),
( 'Twelve' ), ( 'Thirteen' ), ( 'Fourteen'),
( 'Fifteen' ), ('Sixteen' ), ( 'Seventeen'),
('Eighteen' ), ( 'Nineteen' )
INSERT #Below100 VALUES ('Twenty'), ('Thirty'),('Forty'), ('Fifty'),
('Sixty'), ('Seventy'), ('Eighty'), ('Ninety')
DECLARE #English varchar(1024) =
(
SELECT Case
WHEN #Number = 0 THEN ''
WHEN #Number BETWEEN 1 AND 19
THEN (SELECT Word FROM #Below20 WHERE ID=#Number)
WHEN #Number BETWEEN 20 AND 99
-- SQL Server recursive function
THEN (SELECT Word FROM #Below100 WHERE ID=#Number/10)+ '-' +
dbo.fnMoneyToEnglish( #Number % 10)
WHEN #Number BETWEEN 100 AND 999
THEN (dbo.fnMoneyToEnglish( #Number / 100))+' Hundred '+
dbo.fnMoneyToEnglish( #Number % 100)
WHEN #Number BETWEEN 1000 AND 999999
THEN (dbo.fnMoneyToEnglish( #Number / 1000))+' Thousand '+
dbo.fnMoneyToEnglish( #Number % 1000)
WHEN #Number BETWEEN 1000000 AND 999999999
THEN (dbo.fnMoneyToEnglish( #Number / 1000000))+' Million '+
dbo.fnMoneyToEnglish( #Number % 1000000)
ELSE ' INVALID INPUT' END
)
SELECT #English = RTRIM(#English)
SELECT #English = RTRIM(LEFT(#English,len(#English)-1))
WHERE RIGHT(#English,1)='-'
IF ##NestLevel = 1
BEGIN
SELECT #English = #English+' POINT '
SELECT #English = #English+
convert(varchar,convert(int,100*(#Money - #Number)))
END
RETURN (#English)
END

So I just learned Spanish and did it myself. Enjoy!
ALTER FUNCTION [dbo].[fnMoneyToSpanish](#Money AS money)
RETURNS VARCHAR(1024)
AS
BEGIN
DECLARE #Number as BIGINT
SET #Number = FLOOR(#Money)
DECLARE #Below20 TABLE (ID int identity(0,1), Word varchar(32))
DECLARE #Below100 TABLE (ID int identity(2,1), Word varchar(32))
INSERT #Below20 (Word) VALUES
( 'cero'), ('uno'),( 'dos' ), ( 'tres'),
( 'cuatro' ), ( 'cinco' ), ( 'seis' ), ( 'siete' ),
( 'ocho'), ( 'nueve'), ( 'diez'), ( 'once' ),
( 'doce' ), ( 'trece' ), ( 'catorce'),
( 'quince' ), ('dieciséis' ), ( 'diecisiete'),
('dieciocho' ), ( 'diecinueve' )
INSERT #Below100 VALUES ('veinti'), ('treinta'),('cuarenta,'), ('cincuenta'),
('sesenta'), ('setenta'), ('ochenta'), ('coventa')
DECLARE #English varchar(1024) =
(
SELECT Case
WHEN #Number = 0 THEN ''
WHEN #Number BETWEEN 1 AND 19
THEN (SELECT Word FROM #Below20 WHERE ID=#Number)
WHEN #Number BETWEEN 20 AND 99
THEN (SELECT CASE WHEN WORD = 'veinti' AND #Number = '20' THEN 'viente' ELSE WORD END FROM(SELECT Word FROM #Below100 WHERE ID=#Number/10)d) + CASE WHEN ##NestLevel in (3,4) AND (SELECT Word FROM #Below100 WHERE ID=#Number/10) <> 'veinti' THEN ' y ' ELSE '' END + --concat(' Number:',#Number,' Level:', ##NestLevel, ' ') +
dbo.fnMoneyToSpanish( #Number % 10)
WHEN #Number BETWEEN 100 AND 999
THEN CASE WHEN #Number < 200 THEN ' ciento ' ELSE (dbo.fnMoneyToSpanish( #Number / 100)) + 'cientos ' END +
dbo.fnMoneyToSpanish( #Number % 100)
WHEN #Number BETWEEN 1000 AND 999999
THEN CASE WHEN #Number < 2000 THEN ' mil ' ELSE (dbo.fnMoneyToSpanish( #Number / 1000)) + ' mil ' END +
dbo.fnMoneyToSpanish( #Number % 1000)
WHEN #Number BETWEEN 1000000 AND 999999999
THEN CASE WHEN #Number < 200000 THEN ' millón ' ELSE (dbo.fnMoneyToSpanish( #Number / 1000000)) + ' millones ' END +
dbo.fnMoneyToSpanish( #Number % 1000000)
ELSE ' INVALID INPUT' END
)
SELECT #English = RTRIM(#English)
SELECT #English = RTRIM(LEFT(#English,len(#English)-1))
WHERE RIGHT(#English,1)=' y '
IF ##NestLevel = 1
BEGIN
SELECT #English = #English+' dólares y '
SELECT #English = #English+
convert(varchar,convert(int,100*(#Money - #Number))) +' cents'
END
RETURN (ltrim(#English))
END
--select [dbo].[fnMoneyToSpanish](2654876.36)

Related

Find closest second to a given datetimeoffset

I understand that there have been many resources about this already, for example Find closest date in SQL Server but I don't think this is a duplicate because it goes into much more depth due to the requirement.
I need to find a record closest to a given date/time/offset either in the past or in the future to the nearest second.
IF ( OBJECT_ID( N'[dbo].[MYTIMETABLE]' ) IS NOT Null )
DROP TABLE [dbo].[MYTIMETABLE];
GO
CREATE TABLE [dbo].[MYTIMETABLE]
(
[TIMESTAMP] datetimeoffset(0) NOT NULL,
[VALUE] char(3) NOT NULL
);
GO
Fill the table with some records, in my instance, there will eventually be millions of records, hence why this requirement is a little more complicated.
SET NOCOUNT ON;
GO
WHILE ( ( SELECT COUNT(*) FROM [dbo].[MYTIMETABLE] ) < 1000 )
BEGIN
DECLARE #Script nvarchar(max) =
N'INSERT INTO [dbo].[MYTIMETABLE] ( [TIMESTAMP], [VALUE] )
SELECT DATEADD( ' +
CASE ( FLOOR( ( RAND() * 4 ) + 1 ) )
WHEN 1 THEN N'second'
WHEN 2 THEN N'minute'
WHEN 3 THEN N'hour'
WHEN 4 THEN N'day'
END +
N', ' +
CASE ( FLOOR( RAND() * 2 ) )
WHEN 0 THEN N'-'
WHEN 1 THEN N''
END +
CONVERT( nvarchar, FLOOR( ( RAND() * 100 ) + 1 ) ) +
N', SWITCHOFFSET( SYSDATETIMEOFFSET(), ''' +
CASE ( FLOOR( RAND() * 2 ) )
WHEN 0 THEN N'-'
WHEN 1 THEN N'+'
END +
N'0' +
CONVERT( nvarchar, FLOOR( RAND() * 10 ) ) +
N':00'' ) ), ' +
CONVERT( nvarchar, FLOOR( ( RAND() * 100 ) + 1 ) );
--RAISERROR( #Script , 0, 1 ) WITH NOWAIT;
EXEC sp_executesql #Script;
END
GO
The lookup script I came up with:
DECLARE #DateTime datetimeoffset(0) = SYSDATETIMEOFFSET();
SELECT TOP(1) [Current Time] = #DateTime, [Time Difference] = DATEDIFF( second, [TIMESTAMP], #DateTime ), *
FROM [dbo].[MYTIMETABLE]
ORDER BY ABS( DATEDIFF( second, [TIMESTAMP], #DateTime ) );
My question is, is this the most optimal version of this script? It looks very basic and I am worried that once this goes into production and it's running thousands of lookups per day against millions of records that it's going to suffer performance problems.
The script will be housed in a function so that it can be compiled to further optimise it but any additional performance improvement advice would be highly appreciated.

Counting words in column

i must get count number the tag
<name></name>
in column.
<users><name>Tomek</name><name>Pawel</name><name>Krzysiek</name></users>
In this example data, queries should return 3.
Using XPath you can easily implement the logic.
Example XPath for your scenario : count(/users/name)
Result : 3
Test Here
Dynamic sql solution:
DECLARE #Table TABLE (Names NVARCHAR(1100))
INSERT INTO #Table VALUES
('<users><name>Tomek</name><name>Pawel</name><name>Krzysiek</name></users>'),
('<users><name>Tomek</name><name>Pawel</name><name>Krzysiek</name></users>'),
('<users><name>Tomek</name><name>Pawel</name><name>Krzysiek</name></users>')
DECLARE #Sql NVARCHAR(MAX)
SET #Sql = ''
SELECT
#Sql = #Sql +
REPLACE(
REPLACE(
REPLACE(
REPLACE(Names,'</name>',''' as Names UNION ALL ')
,'<name>','SELECT ''')
,'</users>','')
,'<users>','')+CHAR(10)
FROM #Table
SET #Sql = LEFT(#Sql,LEN(#Sql)-11)
SET #Sql = 'SELECT COUNT(Names) AS Names FROM (' + #Sql + ') as AllNames'
EXEC(#Sql)
if you work with xml data then try this variant
DECLARE #XMLdata XML = N'<users><name>Tomek</name><name>Pawel</name><name>Krzysiek</name></users>'
SELECT COUNT(*)
FROM #XMLdata.nodes('/users/name') col ( name )
This variant can be usefull when data storaged like a string (varchar)
--create temp table for testing
IF OBJECT_ID('Tempdb..#Tags') IS NOT NULL
DROP TABLE #Tags
CREATE TABLE #Tags
(
SampleText VARCHAR(1000)
)
INSERT INTO #Tags
( SampleText )
VALUES ( '<users><name>Tomek</name><name>Pawel</name><name>Krzysiek</name></users>' ),
( '<users><name>Somik</name><name>Pawel</name><name>Krzysiek</name></users>' ),
( '<users><name>Krolik</name><name>Pawel</name><name>Krzysiek</name></users>' ),
( '<users><name>Domik</name><name>Pawel</name><name>Krzysiek</name></users>' ),
( '<users><name>Zontik</name><name>Pawel</name><name>Krzysiek</name></users>' );
--------------------------------------------------------------------------------
-- recursive cte for split string
WITH cte
AS ( SELECT n = 1
UNION ALL
SELECT n + 1
FROM cte
WHERE n <= 1000
)
--------------------------------------------------------------------------------
-- final query
SELECT COUNT(*) AS Cnt
FROM cte
JOIN #Tags AS T ON n <= LEN(T.SampleText)
WHERE SUBSTRING(T.SampleText, n, 7) = '</name>'
OPTION ( MAXRECURSION 1000 )

Inherit values from previous records

I have the following table where all fields except Value as a part of a unique index on the table. I'd like to inherit the Value from a record with Value<>NULL to the next revision record only if that record has a NULL value (See example below):
Var[n]Value fields ate varchar and there maybe 1-n number of Var[n]value fields in my table.
Source:
Document# Revision Project# config# Var1Value Var2Value
1744 1 2 1 NULL NULL
1744 2 2 1 NULL NULL
1744 3 2 1 Tit1 ABC
1744 4 2 1 Tit2 ABD
1744 5 2 1 NULL NULL
1744 6 2 1 NULL SDC
1744 7 2 1 AS
1744 8 2 1 Tit3 NULL
Needed result (notice change for revision records 5 and 6 var1value and 5,8 for var2value):
Document# Revision Project# config# Var1Value Var2Value
1744 1 2 1 NULL NULL
1744 2 2 1 NULL NULL
1744 3 2 1 Tit1 ABC
1744 4 2 1 Tit2 ABD
1744 5 2 1 Tit2 ABD
1744 6 2 1 Tit2 SDC
1744 7 2 1 AS
1744 8 2 1 Tit3 AS
Any idea how to handle it by SQL?
Please advise.
I tried the following:
declare #TableName as VarChar(32) = 'MYTABLE'
declare #SetClause as VarChar(1024)
declare #LWhereClause as VarChar(1024)
declare #RWhereClause as VarChar(1024)
-- Get the column names.
select Column_Name
into #Columns
from Information_Schema.Columns
where Table_Name = #TableName and Column_Name like 'Var%'
--select * from #Columns
-- Assemble the clauses we'll need for the UPDATE statement.
declare #ColumnName as VarChar(32)
while ( ##RowCount > 0 )
begin
select top 1 #ColumnName = Column_Name
from #Columns
order by Column_Name
set #SetClause = case when #SetClause is NULL then '' else #SetClause + ', ' end +
#ColumnName + ' = Coalesce( L.' + #ColumnName + ', R.' + #ColumnName + ' )'
set #LWhereClause = case when #LWhereClause is NULL then '' else #LWhereClause + ' or ' end +
'L.' + #ColumnName + ' is NULL'
set #RWhereClause = case when #RWhereClause is NULL then '' else #RWhereClause + ' or ' end +
'R.' + #ColumnName + ' is not NULL'
delete from #Columns
where Column_Name = #ColumnName
end
--select #SetClause, #LWhereClause, #RWhereClause
-- Put together the UPDATE statement.
declare #Update as nVarChar(max)
set #Update=''
set #Update=#Update +
'update L set ' + #SetClause + ' from ' + #TableName +
' as L inner join ' + #TableName + ' as R on R.DocId = L.DocId and R.Rev = L.Rev - 1 and R.Proj = L.Proj and R.Conf = L.Conf' +
' where ( ' + #LWhereClause + ' ) and ( ' + #RWhereClause + ' )'
-- Put together the entire loop. This needs work.
declare #Loop as nVarChar(max)
set #Loop =''
set #Loop=#Loop+
'#declare Eleanor as Int = 42;
while ( #Eleanor > 0 ) '
+ #Update + '
set #Eleanor = ##RowCount
end'
--select #Loop
-- Execute it.
exec #Loop
drop table #Columns
and I get the following error on exec loop. Why it is truncating the nvarchar string?
Msg 203, Level 16, State 2, Line 53
The name '#declare Eleanor as Int = 42;
while ( #Eleanor > 0 ) update L set variable104 = Coalesce( L.variable104, R.variable104 ), variable105 = Coalesce( L.variable105, R.variable105 ), variable106 = Coalesce( L.variable106, R.variable106 ), variable107 = Coalesce( L.variable107, R.variable107 ), variable112 = Coalesce( L.variable112, R.variable112 ), variable116 = Coalesce( L.variable116, R.variable116 ), variable119 = Coalesce( L.variable119, R.variable119 ), variable120 = Coalesce( L.variable120, R.variable120 ), variable121 = Coalesce( L.variable121, R.variable121 ), variable122 = Coalesce( L.variable122, R.variable122 ), variable124 = Co' is not a valid identifier.
exact duplicate of :
SQL QUERY replace NULL value in a row with a value from the previous known value
p.s:i don't have privilege to comment.so i wrote as answer.
EDIT: Begging your pardon, but I failed to consider the variable columns.
This query will retrieve the columns defined in YourTable:
select Column_Name from Information_Schema.Columns where Table_Name = 'YourTable'
Thereafter, you need to build a dynamic query and EXEC it. You could craft code like that shown below for each individual column, or to process all of the columns in one go.
Building the query dynamically is a somewhat tedious process. The following should get you well on your way.
declare #TableName as VarChar(32) = 'YourTable'
declare #SetClause as VarChar(1024)
declare #LWhereClause as VarChar(1024)
declare #RWhereClause as VarChar(1024)
-- Get the column names.
select Column_Name
into #Columns
from Information_Schema.Columns
where Table_Name = #TableName and Column_Name like 'Var%'
select * from #Columns
-- Assemble the clauses we'll need for the UPDATE statement.
declare #ColumnName as VarChar(32)
while ( ##RowCount > 0 )
begin
select top 1 #ColumnName = Column_Name
from #Columns
order by Column_Name
set #SetClause = case when #SetClause is NULL then '' else #SetClause + ', ' end +
#ColumnName + ' = Coalesce( L.' + #ColumnName + ', R.' + #ColumnName + ' )'
set #LWhereClause = case when #LWhereClause is NULL then '' else #LWhereClause + ' or ' end +
'L.' + #ColumnName + ' is NULL'
set #RWhereClause = case when #RWhereClause is NULL then '' else #RWhereClause + ' or ' end +
'R.' + #ColumnName + ' is not NULL'
delete from #Columns
where Column_Name = #ColumnName
end
select #SetClause, #LWhereClause, #RWhereClause
-- Put together the UPDATE statement.
declare #Update as VarChar(4096) =
'update L set ' + #SetClause + ' from ' + #TableName +
' as L inner join ' + #TableName + ' as R on R.DocId = L.DocId and R.Rev = L.Rev - 1 and R.Proj = L.Proj and R.Conf = L.Conf' +
' where ( ' + #LWhereClause + ' ) and ( ' + #RWhereClause + ' )'
-- Put together the entire loop. This needs work.
declare #Loop as VarChar(4096) =
'#declare Eleanor as Int = 42; ...' + #Update + '...'
select #Loop
-- Execute it.
exec #Loop
drop table #Columns
Here's one dreadful way that is based on revision numbers being dense:
declare #Docs as Table ( DocId Int, Rev Int, Proj Int, Conf Int, Var1 VarChar(10) Null, Var2 VarChar(10) Null )
insert into #Docs ( DocId, Rev, Proj, Conf, Var1, Var2 ) values
( 1744, 1, 2, 1, NULL, NULL ),
( 1744, 2, 2, 1, NULL, NULL ),
( 1744, 3, 2, 1, 'Tit1', 'ABC' ),
( 1744, 4, 2, 1, 'Tit2', 'ABD' ),
( 1744, 5, 2, 1, NULL, NULL ),
( 1744, 6, 2, 1, NULL, 'SDC' ),
( 1744, 7, 2, 1, '', 'AS' ), -- The example data for this row is unclear.
( 1744, 8, 2, 1, 'Tit3', NULL )
select * from #Docs
declare #Eleanor as Int = 42
while ( #Eleanor > 0 )
begin
update L
set Var1 = Coalesce( L.Var1, R.Var1 ), Var2 = Coalesce( L.Var2, R.Var2 )
from #Docs as L inner join
#Docs as R on R.DocId = L.DocId and R.Rev = L.Rev - 1 and R.Proj = L.Proj and R.Conf = L.Conf
where ( L.Var1 is NULL or L.Var2 is NULL ) and ( R.Var1 is not NULL or R.Var2 is not NULL )
set #Eleanor = ##RowCount
end
select * from #Docs

spltting the row data with cursor and insert data into different columns

Hi I have string like
'spla - asqlserver 2008 #P1 Q TY 1
:SPLA-WINSV QWSRSTDA#P29- 9 QTY2:SP
LA-WINSVMSTDA #P29-999QTY2:SPLA-WINSVRSTD-sqlserver 2008#P 9 9 9 QT Y3:SPLA-WINSVRSTD #P
9-999QTY4 : SPLA-WINSVRSvr3# 59-99 Q TY5:SP -WI Sa 1 #P 999 Q T Y6'
and I wrote function like
ALTER function [dbo].[string_splitting_WORKING]
(
#STR VARCHAR(MAX),
#DELIMITER CHAR(1)
)
RETURNS #Result Table
(
Output varchar(max)
)
as
begin
DECLARE #Index int
declare #Output varchar(max)
select #index = 1
IF #STR IS NULL RETURN
WHILE #Index ! = 0
BEGIN
SELECT #Index = CHARINDEX(#Delimiter, #Str)
IF #Index != 0
SELECT #Output = LEFT(#Str, #Index - 1)
--ltrim(ltrim(substring(#Str,0,charindex('#',#Str))))
--LEFT(#Str, #Index - 1)
ELSE
SELECT #Output = #Str
INSERT INTO #Result(Output) VALUES (#Output)
SELECT #Str = RIGHT(#Str, LEN(#Str) - #Index)
IF LEN(#Str) = 0
BREAK
END
RETURN
END;
when u pass the string into the function like this
select * from [dbo].[string_splitting_WORKING]('SAQa # P 1 9 s-9 9 9 9 1:SPLA-WINSVRSTDA#P29-999QTY2
:SPLA-WINS VMSTDA#P 29-99 9Q TY2:SPLA-WggaerINSVRSTD# P3 9-9 99 9 Q TY3:SPLA-WINSVRSTD#P49-9999QTY4:SPLA-WINSVRSvr3#P59-9999QTY5:SPLA-WINSVRSat1#P69-9999QTY6 :',':')
I am getting output like this
SAQa # P 1 9 s-9 9 9 9 1
SPLA-WINSVRSTDA#P29-999QTY2
SPLA-WINS VMSTDA#P 29-99 9Q TY2
SPLA-WggaerINSVRSTD# P3 9-9 99 9 Q TY3
SPLA-WINSVRSTD#P49-9999QTY4
SPLA-WINSVRSvr3#P59-9999QTY5
SPLA-WINSVRSat1#P69-9999QTY6
I wrote another function
ALTER FUNCTION [dbo].[RowStringSplit]
(
#RowData varchar(8000),
#Delimiter varchar(5)
)
RETURNS #RtnValue table
( ServerName varchar(1000),
PoNumber varchar(500),
Qty varchar(100))
AS
BEGIN
Declare #Cnt int
Set #Cnt = 1
While (Charindex(#Delimiter,#RowData)>0)
Begin
Insert Into #RtnValue (Servername,PoNumber)--,Qty)
Select ServerName = ltrim(ltrim(Substring(#RowData,0,Charindex('#',#RowData)))),
--PoNumber = ltrim(ltrim(substring(#RowData,charindex('#',#RowData)+1,CHARINDEX(':',#RowData)-1))) --1
--PoNumber = substring(#RowData,charindex('#',#RowData)+1,len(#RowData)) --2
PoNumber = substring(#RowData,charindex('#',#RowData)+1,charindex('QTY',REVERSE(#RowData))+ #Cnt)--3
--PoNumber = case charindex('#',#RowData),1) when 0 then #RowData
-- else
-- substring(#RowData,1,charindex('QTY',#RowData,1)-1
--Qty = substring(#RowData,charindex('QTY',#RowData),Charindex(':',#RowData)-26)
Set #RowData = Substring(#RowData,Charindex(#Delimiter,#RowData)+1,len(#RowData))
Set #Cnt = #Cnt + 1
End
Insert Into #RtnValue (ServerName)
Select Data = ltrim(rtrim(#RowData))
Return
END
and I am getting output like this
ServerName PoNumber Qty
------------------------------------------------
spla - asqlserver 2008 P NULL
SPLA-WINSV QWSRSTDA P2 NULL
SP LA-WINSVMSTDA P29 NULL
SPLA-WINSVRSTD-sqlserver 2008 P 9 NULL
SPLA-WINSVRSTD P 9 NULL
SPLA-WINSVRSvr3 59-99 NULL
SP -WI Sa 1 P 99 NULL
but the requirement is if i remove the data from serverName column It has to show blank and If
I increase the data in the serverName column It should increase but I am getting that
but the problem is if I remove or add the data after # until qty that is from p upto qty the reset Its not comming exactly and last I need only number in the qty column
will u help me how to get the proper output by using function or procedure to split that string
from anand
Please try this. Looks like it's returning the correct output for your input string.
CREATE FUNCTION test (#a VARCHAR(1000))
RETURNS #RtnValue table (
ServerName varchar(1000),
PoNumber varchar(500),
Qty varchar(100)
)
AS
BEGIN
DECLARE #part1 VARCHAR(1000)
DECLARE #part2 VARCHAR(1000)
DECLARE #part3 VARCHAR(1000)
DECLARE #i INT
SELECT #a = LTRIM(RTRIM(#a))
SELECT #i = PATINDEX('%#%', #a)
IF #i = 0 SELECT #i = LEN(#a)
SELECT #part1 = LEFT(#a, #i - 1)
SELECT #a = RIGHT(#a, LEN(#a) - #i)
SELECT #a = REPLACE(#a, ' ', '')
SELECT #i = PATINDEX('%QTY%', #a)
IF #i > 0
BEGIN
SELECT #part2 = LEFT(#a, #i - 1)
SELECT #part3 = RIGHT(#a, LEN(#a) - LEN(#part2) - 3)
END
ELSE
SELECT #part2 = #a
INSERT #RtnValue
SELECT #part1, #part2, #part3
RETURN
END
GO
Execute it together with your function named string_splitting_WORKING:
select t.*
from [dbo].[string_splitting_WORKING]('spla - asqlserver 2008 #P1 Q TY 1
:SPLA-WINSV QWSRSTDA#P29- 9 QTY2:SP
LA-WINSVMSTDA #P29-999QTY2:SPLA-WINSVRSTD-sqlserver 2008#P 9 9 9 QT Y3:SPLA-WINSVRSTD #P
9-999QTY4 : SPLA-WINSVRSvr3# 59-99 Q TY5:SP -WI Sa 1 #P 999 Q T Y6'
,':')
CROSS APPLY dbo.test([Output]) t
It outputs:
ServerName PoNumber Qty
------------------------------------------------
spla - asqlserver 2008 P1 1
SPLA-WINSV QWSRSTDA P29-9 2
SP LA-WINSVMSTDA P29-999 2
SPLA-WINSVRSTD-sqlserver 2008 P999 3
SPLA-WINSVRSTD P 9-999 4
SPLA-WINSVRSvr3 59-99 5
SP -WI Sa 1 P999 6

tsql to generate index creation text, then evaluate it

I have a script that creates and populates a bunch of tables from data we receive as
text files. After loading the data into sql server 2008, I want to add indexes. The cut-and-paste-created script I'm using is shown below.
Is
there a way in TSQL to create a function that will generate and then
evaluate the DDL commands? The function would take parameters
of table name, column name(s) and clustered/not. If there would have
to be a separate function for 1 column/2 column indexes, I'd still
be interested.
I'm envisioning something like:
hypotheticalFunction('clustered', 'precCdaEarn', 'account', 'seq')
hypothericalFunction('nonClustered', 'flats', 'vendorAcct')
and that would check if an acct called "idx_acct_seq" exists on table
precCdaEarn, and drop it if so, then create it as clustered index. (dropping it just
in case i change the definition). And create/recreate a nonclustered index on dbo.flats named "idx_vendorAcct"
========= this is what have now using cut & paste, apologies for the erratic spacing ==============
IF EXISTS (Select 'X'
FROM sysindexes
WHERE id = (SELECT OBJECT_ID('precCdaEarn'))
and name = 'idx_account_seq')
DROP index precCdaEarn.idx_account_seq
Create nonclustered index idx_account_seq
ON precCdaEarn(account, seq)
;
IF EXISTS (Select 'X'
FROM sysindexes
WHERE id = (SELECT OBJECT_ID('acct'))
and name = 'idx_c_precAcct_precMod')
DROP index acct.idx_c_precAcct_precMod
CREATE CLUSTERED INDEX index idx_c_precAcct_precMod
ON acct(precAcct, precMod)
AFAIK you cannot create a function for this, but with a stored procedure it's no problems.
Something like:
IF(OBJECT_ID('PRC_CREATE_INDEX', N'P') IS NOT NULL)
DROP PROCEDURE PRC_CREATE_INDEX
GO
CREATE PROCEDURE dbo.PRC_CREATE_INDEX
(
-- Table name to add index to
#pTablename AS VARCHAR(120)
-- Index name to add/change
, #pIndexname AS VARCHAR(120)
-- UNIQUE, CLUSTERED or other definition
, #pNewIndexDef AS VARCHAR(255) = ''
-- Columns in index
, #pNewIndexCols AS VARCHAR(255)
-- Recreate even if same
, #pForceRecreate AS SMALLINT = 0
-- Additional columns for INCLUDE
, #pColumnsInclude AS VARCHAR(255) = ''
)
AS
------------------------------------
-- Variabels
------------------------------------
DECLARE #SQL_DROP AS NVARCHAR(1000)
, #SQL_CREATE AS NVARCHAR(1000)
, #retval AS INTEGER
, #rowcount AS INTEGER
DECLARE #oldDescription AS VARCHAR(255)
, #newUnique AS SMALLINT
, #newClustered AS SMALLINT
, #newIndexDef AS VARCHAR(255)
, #newDescription AS VARCHAR(255)
, #drop_index AS BIT
, #temp AS VARCHAR(MAX)
------------------------------------
-- Initiate
------------------------------------
SET XACT_ABORT ON
SET NOCOUNT ON
-- Table exists?
IF(OBJECT_ID(#pTablename) IS NULL)
BEGIN
PRINT 'Error: Table does not exist: ' + #pTablename +
' (' + 'creation of index ''' + #pTablename + '.' + #pIndexname + ''')'
RETURN (1)
END
-----------------------------------------------------------------------------------------------------
-- New index
-----------------------------------------------------------------------------------------------------
-- Initiate (Remove blanks, get unique/clustered info)
SELECT #newIndexDef = #pNewIndexDef
-- Remove NONCLUSTERED
, #newIndexDef = REPLACE(#newIndexDef, 'nonclustered', '')
-- CLUSTERED?
, #newClustered = CASE WHEN PATINDEX('%clustered%', LOWER(#newIndexDef)) > 0 THEN 1 ELSE 0 END
, #newIndexDef = REPLACE(#newIndexDef, 'clustered', '')
-- UNIQUE?
, #newUnique = CASE WHEN PATINDEX('%unique%', LOWER(#pNewIndexDef)) > 0 THEN 1 ELSE 0 END
, #newIndexDef = REPLACE(#newIndexDef, 'unique', '')
-- Remove blanks etc.
, #pNewIndexCols = REPLACE(#pNewIndexCols, ' DESC', '#DESC')
, #newIndexDef = REPLACE(#newIndexDef, ' ', '')
, #newIndexDef = REPLACE(#newIndexDef, ',', '')
, #pNewIndexCols = REPLACE(#pNewIndexCols, ' ', '')
, #pColumnsInclude = REPLACE(#pColumnsInclude, ' ', '')
-- Correct defintion?
IF LEN(#newIndexDef) > 0
BEGIN
RAISERROR ('Index defintion ''%s'' is incorrect for index ''%s.%s''!', 11, 11, #pNewIndexDef, #pTablename, #pIndexname)
RETURN (1)
END
IF #newClustered = 1 AND LEN(#pColumnsInclude) > 0
BEGIN
RAISERROR ('Cannot specify included columns for a clustered index (''%s.%s'')!', 11, 11, #pIndexname, #pTablename)
RETURN (1)
END
IF #pNewIndexCols LIKE '%#DESC%'
BEGIN
RAISERROR ('Descending sort direction not supported (''%s.%s'')!', 11, 11, #pTablename, #pIndexname)
RETURN (1)
END
IF #newClustered = 1 AND EXISTS
(
SELECT 1
FROM sys.indexes (NOLOCK)
WHERE object_id = OBJECT_ID(#pTablename, N'U')
AND name <> #pIndexname
AND type = 1 -- CLUSTERED
AND is_hypothetical = 0
)
BEGIN
SELECT #temp = name
FROM sys.indexes (NOLOCK)
WHERE object_id = OBJECT_ID(#pTablename, N'U')
AND name <> #pIndexname
AND type = 1 -- CLUSTERED
AND is_hypothetical = 0
RAISERROR ('Cannot add CLUSTERED index (''%s.%s'') - table already has a clustered index (''%s'')!', 11, 11, #pTablename, #pIndexname, #temp)
RETURN (1)
END
-- Prepare SQL
-- CREATE
SELECT #SQL_CREATE = ''
+ 'CREATE '
+ CASE WHEN #newUnique = 1 THEN 'UNIQUE ' ELSE '' END
+ CASE WHEN #newClustered = 1 THEN 'CLUSTERED ' ELSE 'NONCLUSTERED ' END
+ 'INDEX ' + #pIndexname + ' '
+ 'ON ' + #pTablename + ' '
+ '(' + #pNewIndexCols + ')'
+ CASE WHEN LEN(#pColumnsInclude) > 0 THEN ' INCLUDE (' + #pColumnsInclude + ')' ELSE '' END
-- Description
SELECT #newDescription = ''
+ CASE WHEN #newUnique = 1 THEN 'UNIQUE ' ELSE '' END
+ CASE WHEN #newClustered = 1 THEN 'CLUSTERED ' ELSE 'NONCLUSTERED ' END
+ '(' + #pNewIndexCols + ')'
+ CASE WHEN LEN(#pColumnsInclude) > 0 THEN ' INCLUDE (' + #pColumnsInclude + ')' ELSE '' END
-- DROP
SET #SQL_DROP = ''
+ 'DROP INDEX ' + #pTablename
+ '.' + #pIndexname
-----------------------------------------------------------------------------------------------------
-- Current index
-----------------------------------------------------------------------------------------------------
-- Initiate
SELECT #drop_index = 0
-- Get definition/description and check if recreation is needed
IF EXISTS
(
SELECT 1
FROM sys.indexes (NOLOCK)
WHERE object_id = OBJECT_ID(#pTablename, N'U')
AND name = #pIndexname
)
BEGIN
-- Description
SELECT #oldDescription = ''
+ CASE WHEN i.is_unique = 1 THEN 'UNIQUE ' ELSE '' END
+ CASE
WHEN i.type = 1 THEN 'CLUSTERED '
WHEN i.type = 2 THEN 'NONCLUSTERED '
ELSE '? '
END
+ '(' + STUFF(
(
SELECT ',' + COL_NAME(ic.object_id, ic.column_id)
FROM sys.index_columns ic (NOLOCK)
WHERE ic.object_id = i.object_id
AND ic.index_id = i.index_id
AND ic.is_included_column = 0
ORDER BY ic.key_ordinal
FOR XML PATH('')
)
, 1, 1, '') + ')'
+ ISNULL(' INCLUDE (' + STUFF(
(
SELECT ',' + COL_NAME(ic.object_id, ic.column_id)
FROM sys.index_columns ic (NOLOCK)
WHERE ic.object_id = i.object_id
AND ic.index_id = i.index_id
AND ic.is_included_column = 1
ORDER BY ic.index_column_id -- Or column_id???
FOR XML PATH('')
)
, 1, 1, '') + ')', '')
FROM sys.indexes i (NOLOCK)
WHERE i.object_id = OBJECT_ID(#pTablename, N'U')
AND i.name = #pIndexname
-- Exit?
-- If not changed and no force of recreation
IF #oldDescription = #newDescription
AND
#pForceRecreate = 0
BEGIN
RETURN 0
END
-- We should drop current index..
SET #drop_index = 1
END
-----------------------------------------------------------------------------------------------------
-- Execute SQL
-----------------------------------------------------------------------------------------------------
-- Exec
IF #drop_index = 1
EXEC sp_executesql #SQL_DROP
IF LEN(#pNewIndexCols) > 0
EXEC sp_executesql #SQL_CREATE
-- Message
IF LEN(#pNewIndexCols) > 0 AND #drop_index = 0
PRINT 'Created index ' + #pTablename + '.' + #pIndexname + ' (' + #newDescription + ')'
ELSE IF LEN(#pNewIndexCols) > 0 AND #drop_index = 1
PRINT 'Recreated index ' + #pTablename + '.' + #pIndexname + CHAR(10) +
' From: ' + #oldDescription + CHAR(10) +
' To: ' + #newDescription
-- Well, this will perhaps occur when and if this proc is changed...
ELSE IF #drop_index = 1
PRINT 'Removed index ' + #pTablename + '.' + #pIndexname + ' (' + #oldDescription + ')'
-----------------------------------------------------------------------------------------------------
-- Exit
-----------------------------------------------------------------------------------------------------
RETURN (0)
GO
Use it like:
EXEC dbo.PRC_CREATE_INDEX
#pTablename = 'someTable'
, #pIndexname = 'idx_someTable'
, #pNewIndexDef = 'CLUSTERED'
, #pNewIndexCols = 'some, columns, listed'