How to extract dates from a varchar string - sql-server-2005

How to extract dates from a varchar string;
Format of data:
Rates from 16th April 2011 to 31st Aug 2011
I want output as '16/04/2011' and '31/08/2011'
Regards,
Nitin

To do not play with day end part like "d","st" etc, it is better to split your string into words and then analyse it. Double spaces being removed at the beginning.
DECLARE #s NVARCHAR(MAX)= 'Rates from 16th April 2011 to 2st Aug 2011'
SET #s = REPLACE(#s, ' ', ' ')
DECLARE #BegDay VARCHAR(2)= '' ,
#begMonth NVARCHAR(100) ,
#BegYear VARCHAR(4)
DECLARE #EndDay VARCHAR(2) ,
#EndMonth NVARCHAR(100) ,
#EndYear VARCHAR(4)
DECLARE #BegDate DATETIME ,
#EndDate DATETIME
DECLARE #Words TABLE
(
Word NVARCHAR(1000) ,
WordNr INT
) ;
WITH c ( Char, Pos, WordNr )
AS ( SELECT SUBSTRING(#s, 1, 1) CHAR ,
1 ,
CAST(0 AS BIGINT)
UNION ALL
SELECT SUBSTRING(#s, pos + 1, 1) CHAR ,
pos + 1 ,
CASE WHEN SUBSTRING(#s, pos + 1, 1) = SPACE(1)
THEN c.WordNr + 1
ELSE c.WordNr
END
FROM c
WHERE pos < LEN(#s)
)
INSERT INTO #Words
( Word ,
WordNr
)
SELECT RTRIM(LTRIM(SUBSTRING(#s, c.Pos,
ISNULL(c2.Pos, LEN(#s)) - c.Pos + 1))) ,
c.WordNr + 1
FROM ( SELECT *
FROM c
) c
LEFT OUTER JOIN C C2 ON c2.WordNr = c.WordNr + 1
AND c2.Char = ' '
WHERE c.char = ' '
OR c.Pos = 1 ;
WITH c ( Word, [CHAR], Pos )
AS ( SELECT T.Word ,
CAST(SUBSTRING(T.Word, 1, 1) AS NVARCHAR(100)) ,
1
FROM #Words T
WHERE WordNr = 3
UNION ALL
SELECT c.Word ,
CAST(SUBSTRING(c.Word, 1, pos + 1) AS NVARCHAR(100)) ,
pos + 1
FROM c
WHERE ISNUMERIC(SUBSTRING(C.Word, c.pos + 1, 1)) = 1
)
SELECT TOP 1
#BegDay = char
FROM c
ORDER BY pos DESC
SET #begMonth = ( SELECT T.Word
FROM #Words T
WHERE T.WordNr = 4
)
SET #BegYear = ( SELECT T.Word
FROM #Words T
WHERE T.WordNr = 5
)
SET #BegDate = #BegDay + '' + #begMonth + #Begyear ;
WITH c ( Word, [CHAR], Pos )
AS ( SELECT T.Word ,
CAST(SUBSTRING(T.Word, 1, 1) AS NVARCHAR(100)) ,
1
FROM #Words T
WHERE WordNr = 7
UNION ALL
SELECT c.Word ,
CAST(SUBSTRING(c.Word, 1, pos + 1) AS NVARCHAR(100)) ,
pos + 1
FROM c
WHERE ISNUMERIC(SUBSTRING(C.Word, c.pos + 1, 1)) = 1
)
SELECT TOP 1
#EndDay = char
FROM c
ORDER BY pos DESC
SET #EndMonth = ( SELECT T.Word
FROM #Words T
WHERE T.WordNr = 8
)
SET #EndYear = ( SELECT T.Word
FROM #Words T
WHERE T.WordNr = 9
)
SET #EndDate = #EndDay + '' + #EndMonth + #Endyear
SELECT #BegDate Begindate ,
#EndDate EndDate

Related

SQL Server : prevent output of tablename with date later then today

I am trying to display data with 11 months from 11 tables. Each table containing a different months data. The table name contains the date of each table. At the moment my code works in a way that a person has to input a date such as 201404 then it will display all months from 201404 right up to 201503. I need to put a check in place so that if the person types in 201507 it only throws out 201507 up to 201510 as the remaining months do not exists as yet.
This is my code:
alter PROCEDURE stp3
#FirstTableMonth int =0,
#EndMonth datetime
AS
DECLARE
#LinkedServerName sysname = 'SERVER1',
#left int = 0,
#right int= 0,
#STRING VARCHAR(6),
#monthex int = 12,
#yearex int,
#yearstring int,
#DynamicSQL nvarchar(max) = '',
#DynamicSQL2 nvarchar(max) = '',
#OpenQuerySql nvarchar(max),
#Table_Name sysname,
#Table_Name2 sysname,
#TableMonth int,
#TableMonth2 int ,
#CurrentMonth int = 0,
#NextYearMonth int = 1,
#part2 nvarchar(max) = '',
#TwelfthMonth int = 0;
SET #FirstTableMonth= CAST(#FirstTableMonth AS VARCHAR(6))
set #left = cast(left(#FirstTableMonth,4) +1 as varchar(max))
set #right = right(#FirstTableMonth,2) - 1
set #monthex = 11
set #yearex = cast(left(#FirstTableMonth,4) as varchar(max))
set #yearstring = case when right(#FirstTableMonth,2) = '01' then left(#FirstTableMonth,4) + RIGHT( #monthex + LTRIM( RTRIM( right(#FirstTableMonth,2) ) ), 6 ) else #left end
SET #STRING =
case when right(#FirstTableMonth,2) = '01' then #yearstring
when #right < 10 then CAST(#left AS VARCHAR(4)) + RIGHT( '0' + LTRIM( RTRIM( #right ) ), 6 )
else CAST(#left AS VARCHAR(4)) + RIGHT( LTRIM( RTRIM( #right ) ), 6 ) end
WHILE #CurrentMonth < 11 AND #EndMonth < getdate()
BEGIN
SELECT #TableMonth = CASE WHEN (#FirstTableMonth + #CurrentMonth) % 100 < 13 THEN
#FirstTableMonth + #CurrentMonth
ELSE
#FirstTableMonth + 100 - (#FirstTableMonth % 100) + #NextYearMonth
END,
#NextYearMonth = CASE WHEN (#FirstTableMonth + #CurrentMonth) % 100 < 13 THEN
#NextYearMonth
ELSE
#NextYearMonth + 1
END,
#Table_Name = 'XX_'+CAST(#TableMonth as varchar)+'_T' ,
#TableMonth2 = #TableMonth,
#Table_Name2 = 'XX_'+CAST(#TableMonth2 as varchar)+'_T' ,
#DynamicSQL = #DynamicSQL +
'SELECT
*
FROM '+ #Table_Name + '
'+ CASE WHEN #CurrentMonth < 10 THEN ' UNION ALL ' ELSE '' END ,
#DynamicSQL2 = #DynamicSQL2 +
'SELECT
*
FROM '+ #Table_Name2 + '
'
SET #CurrentMonth = #CurrentMonth + 1
IF OBJECT_ID('tempdb..#TEMP') IS NOT NULL BEGIN DROP TABLE #TEMP END
IF OBJECT_ID('tempdb..#Unioned') IS NOT NULL BEGIN DROP TABLE #Unioned END
--last months snapshot
SELECT
*
INTO #UNIONED
FROM OPENQUERY(SERVER1,' SELECT
*
FROM yy_T
'
)
union all
SELECT
*
FROM OPENQUERY(SERVER1,' SELECT
*
FROM yk
'
)
DECLARE #dateAsNumber VARCHAR(8);
SET #dateAsNumber = CONVERT(CHAR(6),GETDATE(), 112);
DECLARE #DateConvertedFromNumber DATETIME;
SELECT #DateConvertedFromNumber = CONVERT (DATETIME, CONVERT(CHAR(8), #dateAsNumber + '01'))
select
*
LEFT(CONVERT(VARCHAR, DATEADD(MONTH, -1,#DateConvertedFromNumber), 112),6) AS 'Period'
INTO #temp
from #Unioned
drop table OpenQTable
--if equal to month + 99 = previous month then union 11 tables with last months snapshot
--else
--union 11 tables with 12th table
SET #OpenQuerySql = 'IF (' + cast(#FirstTableMonth as varchar) + '+99) = CONVERT(nvarchar(6), dateadd(month,-1,GETDATE()), 112)
BEGIN
SELECT *
FROM OPENQUERY(['+ #LinkedServerName +'], '''+ #DynamicSQL + ''' )
UNION ALL
SELECT *
FROM #temp
END
else
begin
SELECT *
FROM OPENQUERY(['+ #LinkedServerName +'], '''+ #DynamicSQL + ''' )
UNION ALL
SELECT *
FROM OPENQUERY(['+ #LinkedServerName +'], '''+ #DynamicSQL2 + ''' )
end
'
END
insert into OpenQTable
EXECUTE sp_executesql #OpenQuerySql
go
}

MSSQL - Create table function, return substring

I need to create this table function. The function needs to return single words from passed parameters like: hello, hhuu, value
The table function should return:
hello,
hhuu,
value
But I am always getting some errors, please could you help me?
you can write as:
DECLARE #input_char VARCHAR(255)
SET #input_char = 'hello, hhuu, value'
;WITH cte AS (
SELECT
CAST('<r>' + REPLACE(#input_char, ' ', '</r><r>') + '</r>' AS XML) AS input_char
)
SELECT
rtrim( LTRIM (xTable.xColumn.value('.', 'VARCHAR(MAX)')) ) AS input_char
FROM cte
CROSS APPLY input_char.nodes('//r') AS xTable(xColumn)
Please give a look at this article:
http://www.codeproject.com/Tips/625872/Convert-a-CSV-delimited-string-to-table-column-in
and you could use ' ' (space) as delimiter.
SELECT * FROM dbo.CSVtoTable('hello, hhuu, value', ' ')
I have used the following function many times. It is a bit lengthy forgive me, but it has become a great tool for me.
CREATE Function [dbo].[ParseText2Table]
(
#p_SourceText varchar(MAX)
,#p_Delimeter varchar(100) = ',' --default to comma delimited.
)
RETURNS #retTable TABLE
(
POSITION INT
,Int_Value bigint
,Num_value REAL--Numeric(18,3)
,txt_value varchar(MAX)
)
AS
BEGIN
DECLARE #tmpTable TABLE
(
Position2 INT IDENTITY(1,1) PRIMARY KEY
,Int_Value bigint
,Num_value REAL--Numeric(18,3)
,txt_value varchar(MAX)
)
DECLARE #w_Continue INT
,#w_StartPos INT
,#w_Length INT
,#w_Delimeter_pos INT
,#w_tmp_int bigint
,#w_tmp_num REAL--numeric(18,3)
,#w_tmp_txt varchar(MAX)
,#w_Delimeter_Len INT
IF len(#p_SourceText) = 0
BEGIN
SET #w_Continue = 0 -- force early exit
END
ELSE
BEGIN
-- if delimiter is ' ' change
IF #p_Delimeter = ' '
BEGIN
SET #p_SourceText = replace(#p_SourceText,' ','ÿ')
SET #p_Delimeter = 'ÿ'
END
-- parse the original #p_SourceText array into a temp table
SET #w_Continue = 1
SET #w_StartPos = 1
SET #p_SourceText = RTRIM( LTRIM( #p_SourceText))
SET #w_Length = DATALENGTH( RTRIM( LTRIM( #p_SourceText)))
SET #w_Delimeter_Len = len(#p_Delimeter)
END
WHILE #w_Continue = 1
BEGIN
SET #w_Delimeter_pos = CHARINDEX( #p_Delimeter
,(SUBSTRING( #p_SourceText, #w_StartPos
,((#w_Length - #w_StartPos) + #w_Delimeter_Len)))
)
IF #w_Delimeter_pos > 0 -- delimeter(s) found, get the value
BEGIN
SET #w_tmp_txt = LTRIM(RTRIM( SUBSTRING( #p_SourceText, #w_StartPos
,(#w_Delimeter_pos - 1)) ))
IF dbo.isReallyNumeric(#w_tmp_txt) = 1 --and not #w_tmp_txt in('.', '-', '+', '^')
BEGIN
--set #w_tmp_int = cast( cast(#w_tmp_txt as real) as bigint)--numeric) as bigint)
SET #w_tmp_int = CASE WHEN (CAST(#w_tmp_txt AS REAL) BETWEEN -9223372036854775808 AND 9223372036854775808) THEN CAST( CAST(#w_tmp_txt AS REAL) AS bigint) ELSE NULL END
SET #w_tmp_num = CAST( #w_tmp_txt AS REAL)--numeric(18,3))
END
ELSE
BEGIN
SET #w_tmp_int = NULL
SET #w_tmp_num = NULL
END
SET #w_StartPos = #w_Delimeter_pos + #w_StartPos + (#w_Delimeter_Len- 1)
END
ELSE -- No more delimeters, get last value
BEGIN
SET #w_tmp_txt = LTRIM(RTRIM( SUBSTRING( #p_SourceText, #w_StartPos
,((#w_Length - #w_StartPos) + #w_Delimeter_Len)) ))
IF dbo.isReallyNumeric(#w_tmp_txt) = 1 --and not #w_tmp_txt in('.', '-', '+', '^')
BEGIN
--set #w_tmp_int = cast( cast(#w_tmp_txt as real) as bigint)--as numeric) as bigint)
SET #w_tmp_int = CASE WHEN (CAST(#w_tmp_txt AS REAL) BETWEEN -9223372036854775808 AND 9223372036854775808) THEN CAST( CAST(#w_tmp_txt AS REAL) AS bigint) ELSE NULL end
SET #w_tmp_num = CAST( #w_tmp_txt AS REAL)--numeric(18,3))
END
ELSE
BEGIN
SET #w_tmp_int = NULL
SET #w_tmp_num = NULL
END
SELECT #w_Continue = 0
END
INSERT INTO #tmpTable VALUES( #w_tmp_int, #w_tmp_num, #w_tmp_txt )
END
INSERT INTO #retTable SELECT Position2, Int_Value ,Num_value ,txt_value FROM #tmpTable
RETURN
END
Here are the supporting functions for above as well:
CREATE FUNCTION dbo.isReallyInteger
(
#num VARCHAR(64)
)
RETURNS BIT
BEGIN
IF LEFT(#num, 1) = '-'
SET #num = SUBSTRING(#num, 2, LEN(#num))
RETURN CASE
WHEN PATINDEX('%[^0-9-]%', #num) = 0
AND CHARINDEX('-', #num) <= 1
AND #num NOT IN ('.', '-', '+', '^')
AND LEN(#num)>0
AND #num NOT LIKE '%-%'
THEN
1
ELSE
0
END
END
CREATE FUNCTION dbo.isReallyNumeric
(
#num VARCHAR(64)
)
RETURNS BIT
BEGIN
IF LEFT(#num, 1) = '-'
SET #num = SUBSTRING(#num, 2, LEN(#num))
DECLARE #pos TINYINT
SET #pos = 1 + LEN(#num) - CHARINDEX('.', REVERSE(#num))
RETURN CASE
WHEN PATINDEX('%[^0-9.-]%', #num) = 0
AND #num NOT IN ('.', '-', '+', '^')
AND LEN(#num)>0
AND #num NOT LIKE '%-%'
AND
(
((#pos = LEN(#num)+1)
OR #pos = CHARINDEX('.', #num))
)
THEN
1
ELSE
0
END
END
Usage Examples:
--Single Character Delimiter
--select * from dbo.ParseText2Table('100|120|130.56|Yes|Cobalt|Blue','|')
--select txt_value from dbo.ParseText2Table('100 120 130.56 Yes Cobalt Blue',' ') where Position = 3
--select * from dbo.ParseText2Table('100,120,130.56,Yes,Cobalt,Blue,,',',')
/*
POSITION Int_Value Num_value txt_value
----------- ----------- -------------------- --------------
1 100 100.000 100
2 120 120.000 120
3 131 130.560 130.56
4 NULL NULL Yes
5 NULL NULL Cobalt Blue
*/

Split numbers from string

I have my table like this
4-Documento d’identità-3-Attestato di Rischio-2-Carta di Circolazione
10-Contrassegno
12-Documenti di annullo polizza-10-Contrassegno
10-Contrassegno
12-Documenti di annullo polizza-10-Contrassegno
I want to split every row to be like this
4-3-2
10
12-10
10
12-10
try this
DECLARE #table AS TABLE
(
ID INT IDENTITY(1, 1) ,
SomeText VARCHAR(500)
)
INSERT INTO #table
( SomeText )
VALUES ( '4-Documento d’identità-3-Attestato di Rischio-2-Carta di Circolazione' ),
( '10-Contrassegno' ),
( '12-Documenti di annullo polizza-10-Contrassegno' ),
( '10-Contrassegno' ),
( '12-Documenti di annullo polizza-10-Contrassegno' );
WITH cte
AS ( SELECT n = 1
UNION ALL
SELECT n + 1
FROM cte
WHERE n <= 100
),
SplitToChr
AS ( SELECT T.ID ,
SUBSTRING(T.SomeText, cte.n, 1) AS Chr
FROM #table AS T
JOIN cte ON DATALENGTH(T.SomeText) >= cte.n
AND SUBSTRING(T.SomeText, cte.n, 1) LIKE '[0-9-]'
)
SELECT T.ID ,
REVERSE(SUBSTRING(REVERSE(T.FInal),2,LEN(T.FInal))) AS Final
FROM ( SELECT DISTINCT
o.ID ,
REPLACE(( SELECT '' + chr
FROM SplitToChr AS i
WHERE i.id = o.id
FOR
XML PATH('')
), '--', '-') AS FInal
FROM SplitToChr AS o
) AS T
SAMPLE TABLE
CREATE TABLE #TEMP(STRINGCOLUMN NVARCHAR(MAX))
INSERT INTO #TEMP
SELECT '4-Documento d’identità-3-Attestato di Rischio-2-Carta di Circolazione'
UNION ALL
SELECT '10-Contrassegno'
UNION ALL
SELECT '12-Documenti di annullo polizza-10-Contrassegno'
UNION ALL
SELECT '10-Contrassegno'
UNION ALL
SELECT '12-Documenti di annullo polizza-10-Contrassegno'
You can use a function in case you need the query to be simple
Function
CREATE FUNCTION [dbo].[ConvertToNumber]
(#strAlphaNumeric VARCHAR(256))
RETURNS VARCHAR(256)
AS
BEGIN
SET #strAlphaNumeric = LEFT(#strAlphaNumeric,
LEN(#strAlphaNumeric) - CHARINDEX('-', REVERSE(#strAlphaNumeric)) + 0)
DECLARE #intAlpha INT
SET #intAlpha = PATINDEX('%[^0-9]-%', #strAlphaNumeric)
BEGIN
WHILE #intAlpha > 0
BEGIN
SET #strAlphaNumeric = STUFF(#strAlphaNumeric, #intAlpha, 1, '' )
SET #intAlpha = PATINDEX('%[^0-9]-%', #strAlphaNumeric )
END
END
RETURN ISNULL(#strAlphaNumeric,0)
END
And your final query
SELECT DBO.ConvertToNumber(STRINGCOLUMN)STRINGCOLUMN
FROM #TEMP
RESULT
first use this split func
CREATE FUNCTION [dbo].[fnSplitString] ( #string NVARCHAR(MAX)
, #delimiter CHAR(1) )
RETURNS #output TABLE ( splitdata NVARCHAR(MAX) )
BEGIN
DECLARE #start INT
, #end INT
SELECT #start = 1
, #end = CHARINDEX(#delimiter, #string)
WHILE #start < LEN(#string) + 1
BEGIN
IF #end = 0
SET #end = LEN(#string) + 1
INSERT INTO #output ( splitdata )
VALUES ( SUBSTRING(#string, #start, #end - #start) )
SET #start = #end + 1
SET #end = CHARINDEX(#delimiter, #string, #start)
END
RETURN
END
and then use this code
CREATE TABLE #t10 ( id int identity(1,1)
, num int )
INSERT INTO #t10
select *
from dbo.fnSplitString ('4-Documento d’identità-3-Attestato di Rischio-2-Carta di Circolazione','-')
WHERE ISNUMERIC(splitdata)=1
DECLARE #x int = 1
DECLARE #y varchar(10)=''
DECLARE #q varchar(10)
WHILE #x<=(SELECT count(*)
from #t10)
BEGIN
SET #q=(SELECT num
FROM #t10
WHERE id = #x )
set #y=#y+#q+'-'
SET #x=#x+1
END
SELECT left(ltrim(#y),len(#y)-1)
--SELECT * FROM #t10
--drop table #t10
It's a bit messy, but it gets the job done :-)
CREATE TABLE dbo.Documents (
Details VARCHAR(1000)
);
GO
INSERT INTO dbo.Documents
VALUES ('4-Documento d’identità-3-Attestato di Rischio-2-Carta di Circolazione'),
('10-Contrassegno'),
('12-Documenti di annullo polizza-10-Contrassegno'),
('10-Contrassegno'),
('12-Documenti di annullo polizza-10-Contrassegno');
SELECT CASE WHEN (FirstLine + SecondLine + ThirdLine) LIKE '%-'
THEN LEFT(FirstLine + SecondLine + ThirdLine, LEN(FirstLine + SecondLine + ThirdLine) -1)
ELSE (FirstLine + SecondLine + ThirdLine) END AS CleanData
FROM (
SELECT
LEFT(Details, CHARINDEX('-', Details)) AS FirstLine,
REPLACE(CASE
WHEN REPLACE( REPLACE( LEFT(Details,CHARINDEX('-', Details, CHARINDEX('-', Details )+1)+2) , LEFT(Details, CHARINDEX('-', Details, CHARINDEX('-', Details)+1)), ''), LEFT(Details, CHARINDEX('-', Details)), '') LIKE '%[0-9]' THEN REPLACE( REPLACE( LEFT(Details,CHARINDEX('-', Details, CHARINDEX('-', Details )+1)+2) , LEFT(Details, CHARINDEX('-', Details, CHARINDEX('-', Details)+1)), ''), LEFT(Details, CHARINDEX('-', Details)), '') + '-'
ELSE REPLACE( LEFT(Details,CHARINDEX('-', Details, CHARINDEX('-', Details )+1)+2) , LEFT(Details, CHARINDEX('-', Details, CHARINDEX('-', Details)+1)), '')
END , LEFT(Details, CHARINDEX('-', Details)), '') AS SecondLine,
CASE WHEN REVERSE(LEFT(REVERSE(Details), CHARINDEX('-', REVERSE(Details))+2)) LIKE '-%'
THEN LEFT(REVERSE(LEFT(REVERSE(Details), CHARINDEX('-', REVERSE(Details))+2)), 3) ELSE '' END AS ThirdLine
FROM dbo.Documents
) AS A;
Another way of doing it in a much simpler way. Create a function to remove the alphabets from the string
Create FUNCTION dbo.RemoveAlphabets (#string VARCHAR(256))
returns VARCHAR(256)
BEGIN
IF #string IS NULL
RETURN NULL
DECLARE #Result VARCHAR(256)='',#len INT = Len(#string ),#cnt INT=1
WHILE #cnt <= #len
BEGIN
DECLARE #parse INT
SET #parse = Ascii(Substring(#string , #cnt, 1))
IF #parse BETWEEN 48 AND 57 or #parse =45
SET #Result = #Result + Char(#parse)
SET #cnt = #cnt + 1
END
select #result= replace(#Result,'--','-')
RETURN left(#result,case when right(#Result,1)='-' then len(#Result)-1 else len(#result) end)
END
If numbers inside the string is always surrounded by '-' then replace the return statement with this.
RETURN left(#result,len(#Result)-1)
execute the function
select dbo.RemoveAlphabets
('4-Documento d’identità-3-Attestato di Rischio-2-Carta di Circolazione')
Result : 4-3-2

Have someone a function for SQL Server that given a pattern and a value could return a normalized value

I'm programming a function in SQL 2008R2 that i could give it some parameters like a value varchar, a pattern varchar, a separator char and a filler also char. Then I would like to give the value '22687' with the patter '000.000.000.000', a separator '.' and the filler would be '0', then i would like to expect the function will return '000.000.022.687', does any one have a function already done that can do this?
Something like this:
DECLARE #valor VARCHAR(30)
DECLARE #formato VARCHAR(30)
DECLARE #separador CHAR(1)
DECLARE #rellenarcon CHAR(1)
SELECT #valor = '22959'
SELECT #formato = '000.000.000.000'
SELECT #separador = '.'
SELECT #rellenarcon = '0'
DECLARE #n INTEGER
DECLARE #m INTEGER
DECLARE #i INTEGER
DECLARE #j INTEGER
SELECT #n = LEN(#formato)
SELECT #m = LEN(#valor)
SELECT #i = 1
SELECT #j = 1
DECLARE #res2 varchar(30)
SELECT #res2 = ''
SELECT #valor = REVERSE(#valor)
WHILE #i<=#n
BEGIN
if SUBSTRING(#formato,#i,1) <> #separador
begin
IF #j<=#m
BEGIN
SELECT #res2 = #res2 + SUBSTRING(#valor,#j,1)
SELECT #i=#i+1
SELECT #j=#j+1
END
ELSE
BEGIN
SELECT #res2 = #res2 + #rellenarcon
SELECT #i=#i+1
END
end
else
BEGIN
SELECT #res2 = #res2 + #separador
SELECT #i=#i+1
END
END
print reverse(#res2)
Is a crossover code from java to tsql, the original code in java is:
public static String formatear(String valor, String formato, char separator,
char fillWith, Map<Integer, String> params) {
int n = formato.length() - 1;
int m = valor.length() - 1;
int i = n;
int j = m;
StringBuilder res = new StringBuilder(formato);
for(; i >= 0; i--) {
if(res.charAt(i) != separator) {
if(j >= 0) {
res.deleteCharAt(i);
res.insert(i, valor.charAt(j--));
} else {
res.deleteCharAt(i);
res.insert(i, fillWith);
}
}
}
if(params != null) {
Set<Integer> keys = params.keySet();
for(Integer key : keys) {
i = key;
res.deleteCharAt(i);
res.insert(i, params.get(key));
}
}
return res.toString();
}
The following assumes well-formed inputs, e.g. the value is not longer than the pattern.
declare #Pattern as VarChar(64) = '000.000.000.000';
declare #Fill as Char = '0';
declare #Value as VarChar(64) = '22687';
declare #False as Bit = 0;
declare #True as Bit = 1;
with Gargoyle as (
select #Pattern as Pattern, #Value as Value, Cast( '' as VarChar(64) ) as Buffer,
case when Right( #Pattern, 1 ) = #Fill then #True else #False end as Fill
union all
select
-- Always consume a character from the pattern.
Left( Pattern, Len( Pattern ) - 1 ),
-- Consume a character from the value if the pattern contains fill at the current position.
case
when Fill = #True and Value != '' then Left( Value, Len( Value ) - 1 )
else Value end,
-- Add the correct character to the buffer.
Cast( case when Fill = #True and Value != '' then Right( Value, 1 ) else Right( Pattern, 1 ) end + Buffer as VarChar(64) ),
-- Check the next pattern character for fill.
case
when Len( Pattern ) = 1 then #False
when Substring( Pattern, Len( Pattern ) - 1, 1 ) = #Fill then #True
else #False end
from Gargoyle
where Pattern != ''
)
select Buffer
from Gargoyle
where Pattern = '';
Or, as a function:
create function dbo.PatternFill( #Pattern as VarChar(64), #Fill as Char, #Value as VarChar(64) )
returns VarChar(64)
as
begin
declare #Buffer as VarChar(64) = ''
declare #PatternChar as Char = Right( #Pattern, 1 )
declare #ValueChar as Char = Right( #Value, 1 )
while #Pattern != ''
begin
if #PatternChar = #Fill and #ValueChar != ''
begin
-- Replace a fill character with a value character.
select #Buffer = #ValueChar + #Buffer
if Len( #Value ) > 1
select #Value = Left( #Value, Len( #Value ) - 1 ), #ValueChar = Right( #Value, 1 )
else
select #ValueChar = '', #Value = ''
end
else
begin
-- Copy the pattern character.
select #Buffer = #PatternChar + #Buffer
end
if Len( #Pattern ) > 1
select #Pattern = Left( #Pattern, Len( #Pattern ) - 1 ), #PatternChar = Right( #Pattern, 1 )
else
select #PatternChar = '', #Pattern = ''
end
return #Buffer
end
go
declare #Result as VarChar(64)
declare #Count as Int = 1000000
declare #Start as DateTime = GetDate()
while #Count > 0
select #Result = dbo.PatternFill( '000.000.000.000', '0', '22687' ), #Count = #Count - 1
select #Result as [Result], DateDiff( ms, #Start, GetDate() ) as [Total ms]
1,000,000 iterations on my notebook took 151,656ms, but it's busy BOINCing. That's simple timing with no correction for the time consumed by an empty loop or calling an empty function.
This query will do it:
;with cteZeroPadded(Num) as
(
Select Right('000000000000' + '22687', 12)
)
,cteSplit as
(
Select SUBSTRING(Num, 1, 3) Col1
,SUBSTRING(Num, 4, 3) Col2
,SUBSTRING(Num, 7, 3) Col3
,SUBSTRING(Num, 10, 3) Col4
From cteZeroPadded
)
Select Col1 + '.' + Col2 + '.' + Col3 + '.' + Col4
From cteSplit
In SQLServer2005+ you can use option with recursive CTE
DECLARE #valor varchar(30) = '22959',
#formato varchar(30) = '000000000000000',
#text varchar(30),
#result varchar(30) = N''
SET #text = REVERSE(RIGHT(#formato + #valor, 15))
;WITH cte AS
(
SELECT 1 AS Number, SUBSTRING(#text, 1, 1) AS Num
UNION ALL
SELECT c.Number + 1,
CASE WHEN c.Number IN(3, 7, 11) THEN '.' ELSE
SUBSTRING(#text, CASE WHEN c.Number > 11 THEN c.Number - 2
WHEN c.Number > 7 THEN c.Number - 1
WHEN c.Number > 3 THEN c.Number
ELSE c.Number + 1
END, 1)
END
FROM cte c
WHERE Number < LEN(#text)
)
SELECT #result += c.Num
FROM cte c
ORDER BY Number DESC
SELECT #result
See demo on SQLFiddle

Improve case statement in order clause

I have the store sql
ALTER procedure [dbo].[TNNews_User_SearchBasic]
#Title nvarchar(400),
#CategoryId int,
#IsInterested int,
#IsHot int,
#IsTopCategory int,
#IsPublish int,
#PageSize int,
#PageIndex int,
#OrderBy varchar(20),
#PortalId int,
#LanguageId varchar(6)
as
DECLARE #EndTime DATETIME
DECLARE #StartTime DATETIME
SET #StartTime = GETDATE()
declare #tbCategory table(Id int)
DECLARE #StartRowIndex INT
IF #PageSize=0 SELECT #PageSize=count(*) FROM TNNews
IF(#PageIndex<0) SET #PageIndex=0
SET #StartRowIndex = #PageSize*(#PageIndex-1)+1
;WITH tmpCategory(Id, Name,ParentId,Level)
AS (
SELECT
e.Id,
e.Name,
e.ParentId,
1
FROM dbo.TNCategory AS e
WHERE
Id = #CategoryId or (#CategoryId='' and ParentId<=0)
UNION ALL
SELECT
e.Id,
e.Name,
e.ParentId,
Level + 1
FROM dbo.TNCategory AS e
JOIN tmpCategory AS d ON e.ParentId = d.Id
)
insert #tbCategory select Id from tmpCategory
;WITH tmpNews as
(
SELECT
a.Id,a.Title,a.Subject
,ROW_NUMBER() OVER (ORDER BY (Publisheddate) desc) as ThuTuBanGhi
FROM dbo.TNNews a
where 1 = 1
--and ( Title like '%'+#Title+'%')
and (#CategoryId = -1 or exists (select 0 from #tbCategory b where b.Id = a.CategoryId))
and (#IsInterested = -1 or IsIntrested = #IsInterested )
and (#IsHot = -1 or IsHot = #IsHot )
and (#IsTopCategory = -1 or IsTopCategory = #IsTopCategory )
and (#IsPublish = -1 or IsPublished = #IsPublish)
and PortalId=#PortalId
and LanguageId = #LanguageId
)
select *, (select COUNT(Id) from tmpNews) as 'TongSoBanGhi' from tmpNews
WHERE
ThuTuBanGhi BETWEEN (#StartRowIndex) AND (#StartRowIndex + #PageSize-1)
SET #EndTime = GETDATE()
PRINT 'StartTime = ' + CONVERT(VARCHAR(30),#StartTime,121)
PRINT ' EndTime = ' + CONVERT(VARCHAR(30),#EndTime,121)
PRINT ' Duration = ' + STR(DATEDIFF(MILLISECOND,#StartTime,#EndTime)) + ' millisecond'
select STR(DATEDIFF(MILLISECOND,#StartTime,#EndTime))
After this store excute
EXEC [dbo].[TNNews_User_SearchBasic]
#Title='',
#CategoryId = '',
#IsInterested = -1,
#IsHot = -1,
#IsTopCategory = -1,
#IsPublish = -1,
#PageSize = 20,
#PageIndex = 1,
#OrderBy = '',
#PortalId = 0,
#LanguageId = N'vi-VN'
go
The time excute about "200ms". And I create a new store "TNNews_User_SearchBasic1" with some change.
.....
--,ROW_NUMBER() OVER (ORDER BY (Publisheddate) desc) as ThuTuBanGhi
,ROW_NUMBER() OVER (ORDER BY (case when #OrderBy='VIEW_COUNT' then ViewCount else PublishedDate end) desc) as ThuTuBanGhi
.....
and now the time excute this store
EXEC [dbo].[TNNews_User_SearchBasic1]
#Title='',
#CategoryId = '',
#IsInterested = -1,
#IsHot = -1,
#IsTopCategory = -1,
#IsPublish = -1,
#PageSize = 20,
#PageIndex = 1,
#OrderBy = '',
#PortalId = 0,
#LanguageId = N'vi-VN'
GO
about 900ms.
I don't understand why there is a change. Please help me improve these stores.
PS: I put example db at: http://anhquan22.tk/Portals/0/Videos/web.rar
Finished analysis the structure of your database. The part of the problem is hiding in the table structure.
I have prepared a backup for you. In it, I slightly modified scheme to improve performance and some normalize the table. You can download it from this link.
...to your question, I would do like this -
DECLARE #SQL NVARCHAR(1000)
SELECT #SQL = N'
;WITH tmpCategory (Id, Name, ParentId, [Level]) AS
(
SELECT
e.Id
, e.Name
, e.ParentId
, 1
FROM dbo.TNCategory e
WHERE Id = #CategoryId OR (#CategoryId = '''' AND ParentId <= 0)
UNION ALL
SELECT
e.Id
, e.Name
, e.ParentId
, [Level] + 1
FROM dbo.TNCategory e
JOIN tmpCategory d ON e.ParentId = d.Id
)
SELECT
a.Id
, ROW_NUMBER() OVER (ORDER BY ' +
CASE WHEN #OrderBy = 'VIEW_COUNT'
THEN 'ViewCount'
ELSE 'PublishedDate'
END +' DESC) AS ThuTuBanGhi
FROM dbo.TNNewsMain a
where PortalId = #PortalId
AND LanguageId = #LanguageId'
+ CASE WHEN #IsInterested != -1 THEN ' AND IsInterested = #IsInterested' ELSE '' END
+ CASE WHEN #IsHot != -1 THEN ' AND IsHot = #IsHot' ELSE '' END
+ CASE WHEN #IsTopCategory != -1 THEN ' AND IsTopCategory = #IsTopCategory' ELSE '' END
+ CASE WHEN #IsPublish != -1 THEN ' AND IsPublish = #IsPublish' ELSE '' END
+ CASE WHEN #CategoryId != -1 THEN '' ELSE ' AND EXISTS(SELECT 1 FROM tmpCategory b WHERE b.Id = a.CategoryId)' END
INSERT INTO #temp (Id, ThuTuBanGhi)
EXECUTE sp_executesql
#SQL
, N'#PortalId INT
, #LanguageId VARCHAR(6)
, #CategoryId INT
, #IsInterested INT
, #IsHot INT
, #IsTopCategory INT
, #IsPublish INT'
, #PortalId = #PortalId
, #LanguageId = #LanguageId
, #CategoryId = #CategoryId
, #IsInterested = #IsInterested
, #IsHot = #IsHot
, #IsTopCategory = #IsTopCategory
, #IsPublish = #IsPublish;
SELECT
d.Id
, tm.Title
, tm.[Subject]
, d.ThuTuBanGhi
, c.TongSoBanGhi
FROM (
SELECT t.Id
, t.ThuTuBanGhi
FROM #temp t
WHERE t.ThuTuBanGhi BETWEEN #StartRowIndex AND #StartRowIndex + #PageSize - 1
) d
JOIN TNNewsMain tm ON d.Id = tm.Id
CROSS JOIN (
SELECT TongSoBanGhi = (SELECT COUNT(1) FROM #temp)
) c