How to get parent Id from a run time generated row_number() as unique id in SQL - sql-server-2012

I have removed while loop used before but furnishing exact results. Now everything works fine except parentkey.
How to get "parentkey" in the stored procedure perfectly??
The procedure furnishes wrong results if more than 1 same parent is addded in same/different level of bom. The results shows one parent for all childs. It should show both parents separately with childs. The following part does not furnish exact result in case explained above.
(SELECT MAX(e.idcolumn) FROM #tExplode E WHERE e.fcomponent = b.fparent AND e.fcomprev = b.fparentrev AND e.cfacilityid = b.pfacilityid AND e.idcolumn > 0 ) As parentkey
IF EXISTS (SELECT name FROM sysobjects WHERE name = 'M2M_Std_BOM_Explosion' AND type = 'P')
DROP PROCEDURE M2M_Std_BOM_Explosion
GO
CREATE Procedure [dbo].[M2M_Std_BOM_Explosion]
#pcPartNo CHAR(25),
#pcPartRev CHAR(3),
#pcFac CHAR(20),
#plIndent Bit
As
Begin
set nocount on
if object_id('tempdb..#tExplode') is not null drop table #tExplode
declare #sql as varchar(max) = ''
create table #tExplode
(
ID int not null,
lvl int not null,
CompPath varchar(8000),
ItmPath varchar(8000),
display varchar(100),
fcomponent varchar(25),
cfacilityid varchar(20),
fcomprev varchar(3),
fparent varchar(25),
fparentrev varchar(3),
pfacilityid varchar(20),
fitem varchar(6),
fqty numeric(15,5),
fsumqty numeric(15,5),
fst_ef_dt datetime,
fend_ef_dt datetime,
bomid int,
fnoperno int,
fltooling bit,
fbommemo varchar(8000),
fprodcl varchar(20),
flfssvc bit,
idcolumn Integer,
timestamp_column timestamp,
flextend bit,
fcompudrev varchar(3),
fcparudrev varchar(3),
forder int
)
Insert into #tExplode( ID,lvl,CompPath,ItmPath,display,fcomponent,cfacilityid,fcomprev,
fparent,fparentrev,pfacilityid,fitem,fqty,fsumqty,fst_ef_dt,
fend_ef_dt,bomid,fnoperno,fltooling,fbommemo,flfssvc,flextend,
fcompudrev,fcparudrev,forder,idcolumn)
SELECT 1,
0,
Cast(Rtrim(ltrim(#pcFac))+rtrim(ltrim(#pcPartNo))+rtrim(ltrim(#pcPartRev)) as varchar(8000)),
Cast(rtrim(ltrim(#pcFac))+rtrim(ltrim(#pcPartNo))+rtrim(ltrim(#pcPartRev)) as Varchar(8000)),
cast(1 AS varchar(100)),
#pcPartNo,
#pcFac,
#pcPartRev,
'',
'',
'',
'',
1,
1,
{d '1900-01-01'},
{d '1900-01-01'},
0,
'',
0,
'',
0,
0,
'',
'',
0,
0
FROM (SELECT #pcFac fac,
#pcPartNo fpartno,
#pcPartRev frev) x
LEFT JOIN inrtgc i On i.fac=x.fac And i.fpartno=x.fpartno And i.fcpartrev=x.frev
; WITH t AS (
select ID,lvl,CompPath,ItmPath,display,fcomponent,cfacilityid,fcomprev,
fparent,fparentrev,pfacilityid,fitem,fqty,fsumqty,fst_ef_dt,
fend_ef_dt,
bomid,
fnoperno,
fltooling,
fbommemo,
flfssvc,
flextend,
fcompudrev,
fcparudrev,
forder
from #tExplode
UNION ALL
SELECT b.identity_column,
t.lvl+1,
rtrim(ltrim(t.CompPath)) + '!' + rtrim(ltrim(b.pfacilityid)) + rtrim(ltrim(b.fcomponent)) + Replicate(' ',25-Len(rtrim(ltrim(b.fcomponent))))+ rtrim(ltrim(b.fcomprev + '!')),
rtrim(ltrim(t.ItmPath)) + '!' + rtrim(ltrim(b.fitem)),
Cast(replicate(' ',2*t.lvl) + b.fcomponent as Varchar(100)),
Cast(b.fcomponent as Varchar(25)),
Cast(b.cfacilityid as Varchar(20)),
Cast(b.fcomprev as Varchar(3)),
Cast(b.fparent as Varchar(25)),
Cast(b.fparentrev as Varchar(3)),
Cast(b.pfacilityid as Varchar(20)),
Cast(b.fitem as varchar(6)),
b.fqty,
Cast(CASE
WHEN b.flextend = 1 THEN b.fqty * ISNULL(t.fsumqty, cast(1 AS numeric(15,5)))
ELSE b.fqty
END as Numeric(15,5)),
b.fst_ef_dt,
b.fend_ef_dt,
b.identity_column,
ISNULL(b.fnoperno,cast(0 AS int)),
b.fltooling,
Cast(b.fbommemo as Varchar(8000)),
b.flfssvc,
b.flextend,
Cast(b.fcompudrev as Varchar(3)),
Cast(b.fcparudrev as Varchar(3)),
0
FROM inboms b
inner join t ON b.pFacilityID = t.cFacilityID
AND b.fparent = t.fcomponent
AND b.fparentrev = t.fcomprev
)
insert into #tExplode
(ID,lvl,CompPath,ItmPath,display,fcomponent,cfacilityid,fcomprev,
fparent,fparentrev,pfacilityid,fitem,fqty,fsumqty,fst_ef_dt,
fend_ef_dt,
bomid,
fnoperno,
fltooling,fbommemo,flfssvc,
flextend,
fcompudrev,fcparudrev,forder,idcolumn)
select ID,lvl,CompPath,ItmPath,display,fcomponent,cfacilityid,fcomprev,
fparent,fparentrev,pfacilityid,fitem,fqty,fsumqty,fst_ef_dt,
fend_ef_dt,
bomid,
fnoperno,
fltooling,fbommemo,
flfssvc,
flextend,
fcompudrev,fcparudrev,forder,row_number() over (order by lvl,comppath,cfacilityid,fcomponent,fcomprev,forder) from t
Set #sql = 'select b.idcolumn,b.lvl,
b.comppath,
b.itmpath,
b.display,
CASE WHEN m.fluseudrev = 1 THEN b.fcompudrev ELSE b.fcomprev END as disprev,
b.fitem,
fsource,
b.fqty,
CASE when b.flextend = 1 then b.fsumqty ELSE ((b.fqty * ISNULL(b.fsumqty, cast(1 AS numeric(15,5))))/Isnull(i.fSpq,1)) END as fsumqty,
fmeasure,
b.fnoperno,
CASE WHEN inbomm.fpartno IS NULL THEN SPACE(1) ELSE ''*'' END as isparent, --Working
b.fst_ef_dt,
b.fend_ef_dt,
b.bomid,
b.fcomponent,
b.cfacilityid,
b.fcomprev,
m.fdescript as fdesc,
b.fparent,
b.fparentrev,
b.pfacilityid,
ISNULL(m.identity_column, CAST(0 As Int)) as partid,
fidims,
Case When m.fcstscode=''O'' Then ''Obsolete''
Else
CASE WHEN NOT ((b.fst_ef_dt IS NULL) OR (b.fst_ef_dt={d ''1900-01-01''})) AND
DATEDIFF(day,b.fst_ef_dt, GetDate()) < 0
THEN ''Future''
WHEN NOT ((b.fend_ef_dt IS NULL) OR (b.fend_ef_dt={d ''1900-01-01''})) AND
DATEDIFF(day,b.fEnd_ef_dt, GETDATE()) > 0
THEN ''Expired''
ELSE ''Current''
END
End as effectivity,
b.fltooling,
b.fbommemo,
m.fprodcl,
fllotreqd,
b.flfssvc,
b.timestamp_column,
(SELECT MAX(e.idcolumn) FROM #tExplode E WHERE e.fcomponent = b.fparent AND e.fcomprev = b.fparentrev AND e.cfacilityid = b.pfacilityid AND e.idcolumn > 0 ) As parentkey,
b.flextend,
m.fluseudrev,
b.fcompudrev,
b.fcparudrev,
b.forder
from #tExplode b LEFT OUTER JOIN inmastx m ON
b.cFacilityID = m.fac AND
b.fcomponent = m.fpartno AND
b.fcomprev = m.frev
LEFT OUTER JOIN inbomm ON
b.pfacilityid = inbomm.facilityid AND
b.fComponent = InBomm.fPartno AND
b.fCompRev = InBomm.fcPartRev
LEFT OUTER JOIN inrtgc i ON
b.pfacilityid = i.fac AND
b.fComponent = i.fPartno AND
b.fCompRev = i.fcPartRev
where ' + case when #plIndent= 1 then 'b.lvl >= 1' else 'b.lvl = 1' end + '
order by ' + case when #plIndent= 1 then 'b.comppath,b.cfacilityid,b.fcomponent,b.fcomprev,b.forder' else 'b.comppath,b.forder' end + ''
Exec(#sql)
drop table #tExplode
end
--How to get "parentkey" in the stored procedure perfectly??
--The procedure furnishes wrong results if more than 1 same parent is addded in --same/different level of bom. The results shows one parent for all childs.

Related

How to replace where clause with a function and use it in stored procedures?

I have two stored procedures that they have the same where clause, one of them is use for pagination:
ALTER PROCEDURE [dbo].[ret_PayrollCalculations_GetPagedFilteredPersonnels]
(
#SortField varchar(512),
#PageNo int,
#PageSize int,
#CalculationCommandType int,
#StartWorkingPeriodId int,
#StartYear int,
#EndWorkingPeriodId int,
#EndYear int,
#Status int,
#SalariedGuid uniqueidentifier,
#SalariedType int,
#OfficeNumber varchar(64),
#SalariedResultSetId int,
#Keyword nvarchar(2024),
#OperationalUnitIds [dbo].[ListOfID] READONLY
)
AS
DECLARE #AccessibleSalariedGuids [dbo].[ListOfGuid]
IF EXISTS (SELECT * FROM #OperationalUnitIDs)
BEGIN
INSERT INTO #AccessibleSalariedGuids
SELECT FeatureGuid FROM prs_OperationalUnitFeatures
WHERE
OperationalUnitID in (SELECT * FROM #OperationalUnitIDs) AND
FeatureFlag IN (2,4)
END
ELSE BEGIN
INSERT INTO #AccessibleSalariedGuids
SELECT [Guid] FROM ret_vwSalaried
END
DECLARE #OffsetRows INT = CASE WHEN #PageNo = 1 THEN 0 ELSE (#PageNo - 1) * #PageSize END;
DECLARE #TotalCount INT;
WITH Result AS(
SELECT
CASE
WHEN #SortField = N'[FullName]' THEN ROW_NUMBER() OVER (ORDER BY salaried.[FullName])
WHEN #SortField = N'[FullName] DESC' THEN ROW_NUMBER() OVER (ORDER BY salaried.[FullName] DESC)
WHEN #SortField = N'[WorkingPeriodTitle]' THEN ROW_NUMBER() OVER (ORDER BY calcs.[Year],workingPeriods.[Index])
WHEN #SortField = N'[WorkingPeriodTitle] DESC' THEN ROW_NUMBER() OVER (ORDER BY calcs.[Year] DESC,workingPeriods.[Index] DESC)
WHEN #SortField = N'[PersonnelNo]' THEN ROW_NUMBER() OVER (ORDER BY salaried.[PersonnelNo])
WHEN #SortField = N'[PersonnelNo] DESC' THEN ROW_NUMBER() OVER (ORDER BY salaried.[PersonnelNo] DESC)
END AS [RowNumber],
calcs.[Guid],
calcs.[CalculationCommandGuid],
calcs.[SalariedGuid],
salaried.[PersonnelNo],
salaried.[FullName] AS [PersonnelFullName],
command.[Type] [CommandType],
salaried.[SalariedType],
workingPeriods.Title AS [WorkingPeriodTitle],
command.[MainYear] AS [Year],
command.[Approved],
command.[FinalApproved]
FROM
ret_PayrollCalculationCommands command INNER JOIN
ret_PayrollCalculations calcs ON calcs.[CalculationCommandGuid] = command.[Guid] INNER JOIN
ret_vwSalaried salaried ON calcs.[SalariedGuid] = salaried.[Guid] INNER JOIN
prs_workingPeriods workingPeriods ON workingPeriods.[Id] = command.[MainWorkingPeriodID]
WHERE
ISNULL(calcs.[MainCalculation],0) = 1 AND
ISNULL(command.[Deleted],0)=0 AND
(#Keyword = '' OR salaried.PersonnelNo = #Keyword OR salaried.FullName LIKE N'%' + #Keyword + '%' OR salaried.FullNameReversed LIKE N'%' + #Keyword + '%') AND
(ISNULL(#calculationCommandType, 0) = 0 OR command.[Type] = #calculationCommandType) AND
(ISNULL(#StartYear, 0) = 0 OR command.[MainYear] >= #StartYear) AND
(ISNULL(#StartWorkingPeriodId, 0) = 0 OR command.[MainWorkingPeriodID] >= #StartWorkingPeriodId) AND
(ISNULL(#EndYear, 0) = 0 OR command.[MainYear] <= #EndYear) AND
(ISNULL(#EndWorkingPeriodId, 0) = 0 OR command.[MainWorkingPeriodID] <= #EndWorkingPeriodId) AND
(ISNULL(#Status, -1) = -1 OR command.[Approved] = #Status) AND
(ISNULL(#SalariedType, -1) = -1 OR salaried.[SalariedType] = #SalariedType) AND
(ISNULL(#SalariedGuid,'00000000-0000-0000-0000-000000000000') = '00000000-0000-0000-0000-000000000000' OR calcs.[SalariedGuid] = #SalariedGuid) AND
(#OfficeNumber IS NULL OR salaried.[OfficeNumber] LIKE '%'+#OfficeNumber+'%') AND
(ISNULL(#SalariedResultSetId, -1) = -1 OR calcs.[SalariedGuid] IN (SELECT [SalariedGuid] FROM ret_SalariedResultSetItems WHERE SalariedResultSetID = #SalariedResultSetId)) AND
(calcs.[SalariedGuid] IN (SELECT * FROM #AccessibleSalariedGuids))
), TableForTotalCount AS (SELECT COUNT(*) As TotalCount FROM Result)
SELECT
(SELECT TOP 1 TotalCount FROM TableForTotalCount) AS TotalCount,
*
FROM Result
ORDER BY
[RowNumber]
OFFSET #OffsetRows ROWS
FETCH NEXT #PageSize ROWS ONLY
and another one supposed to return some Guids
ALTER PROCEDURE [dbo].[ret_PayrollCalculations_GetFilteredPersonnels]
(
#CalculationCommandType int,
#StartWorkingPeriodId int,
#StartYear int,
#EndWorkingPeriodId int,
#EndYear int,
#Status int,
#SalariedGuid uniqueidentifier,
#SalariedType int,
#OfficeNumber varchar(64),
#SalariedResultSetId int,
#Keyword nvarchar(2024),
#OperationalUnitIds [dbo].[ListOfID] READONLY
)
AS
DECLARE #AccessibleSalariedGuids [dbo].[ListOfGuid]
IF EXISTS (SELECT * FROM #OperationalUnitIDs)
BEGIN
INSERT INTO #AccessibleSalariedGuids
SELECT FeatureGuid FROM prs_OperationalUnitFeatures
WHERE
OperationalUnitID in (SELECT * FROM #OperationalUnitIDs) AND
FeatureFlag IN (2,4)
END
ELSE BEGIN
INSERT INTO #AccessibleSalariedGuids
SELECT [Guid] FROM ret_vwSalaried
END
SELECT
calcs.[Guid]
FROM
ret_PayrollCalculationCommands command INNER JOIN
ret_PayrollCalculations calcs ON calcs.[CalculationCommandGuid] = command.[Guid] INNER JOIN
ret_vwSalaried salaried ON calcs.[SalariedGuid] = salaried.[Guid]
WHERE
ISNULL(calcs.[MainCalculation],0) = 1 AND
ISNULL(command.[Deleted],0)=0 AND
(#Keyword = '' OR salaried.PersonnelNo = #Keyword OR salaried.FullName LIKE N'%' + #Keyword + '%' OR salaried.FullNameReversed LIKE N'%' + #Keyword + '%') AND
(ISNULL(#calculationCommandType, 0) = 0 OR command.[Type] = #calculationCommandType) AND
(ISNULL(#StartYear, 0) = 0 OR command.[MainYear] >= #StartYear) AND
(ISNULL(#StartWorkingPeriodId, 0) = 0 OR command.[MainWorkingPeriodID] >= #StartWorkingPeriodId) AND
(ISNULL(#EndYear, 0) = 0 OR command.[MainYear] <= #EndYear) AND
(ISNULL(#EndWorkingPeriodId, 0) = 0 OR command.[MainWorkingPeriodID] <= #EndWorkingPeriodId) AND
(ISNULL(#Status, -1) = -1 OR command.[Approved] = #Status) AND
(ISNULL(#SalariedType, -1) = -1 OR salaried.[SalariedType] = #SalariedType) AND
(ISNULL(#SalariedGuid,'00000000-0000-0000-0000-000000000000') = '00000000-0000-0000-0000-000000000000' OR calcs.[SalariedGuid] = #SalariedGuid) AND
(#OfficeNumber IS NULL OR salaried.[OfficeNumber] LIKE '%'+#OfficeNumber+'%') AND
(ISNULL(#SalariedResultSetId, -1) = -1 OR calcs.[SalariedGuid] IN (SELECT [SalariedGuid] FROM ret_SalariedResultSetItems WHERE SalariedResultSetID = #SalariedResultSetId)) AND
(calcs.[SalariedGuid] IN (SELECT * FROM #AccessibleSalariedGuids))
When a bug appears I have to fix the problem in both stored procedures, to avoid duplication I wanted Where clauses in a function and call the function in stored procedures,
But I don't know how?
This is how I would approach this:
If you're using Microsoft sql-server, you can make transact-sql code.
Convert your sql procedure into a string and make the Where clause a text variable that you declare elsewhere.
So it's creating a meta- procedure.
For eg.
DECLARE #whereClause LONGTEXT;
DECLARE #SQLString LONGTEXT;
SET #whereClause = 'i=1'
SET #SQLString = 'SELECT * FROM table WHERE' & #whereClause
sp_executesql SQLString

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
*/

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

SQL query - selecting the columns whose grand total is greater than 0

Here is my SQL query:
DROP TABLE #tempi
CREATE TABLE #tempi
(
nav1 VARCHAR(50),
nav2 INT ,
nav3 INT ,
nav4 INT ,
nav5 INT
)
INSERT
INTO #tempi
SELECT COALESCE(CAST(machinename AS VARCHAR(28)), 'Grand Total:') AS machinename ,
SUM(CASE WHEN vfrm.job_id = '1001' THEN DATEDIFF(mi, 0, total_time) END) AS crate_small ,
SUM(CASE WHEN vfrm.job_id = '1002' THEN DATEDIFF(mi, 0, total_time) END) AS crate_medium ,
SUM(CASE WHEN vfrm.job_id = '1014' THEN DATEDIFF(mi, 0, total_time) END) AS front_air_deflector,
SUM(CASE WHEN vfrm.job_id = '9999' THEN DATEDIFF(mi, 0, total_time) END) AS no_schedule
FROM ven_fullreportmaster vfrm
INNER JOIN ven_descriptionmaster vdm
ON vdm.description_id = vfrm..description_id
INNER JOIN ven_machinemaster vm
ON vm.machine_id = vfrm..machine_id
WHERE vfrm.entry_date = CONVERT(VARCHAR, GETDATE()-7, 105)
--and vfrm.shift_id =1
AND vfrm.is_task_completed = 'Y'
GROUP BY machinename WITH ROLLUP
The output in tempi table:
nav1 nav2 nav3 nav4 nav5
abc 0:0 0:0 0:0 1:0
def 0:0 1:0 2:0 1:0
ghi 0:0 0:0 0:0 1:0
grand 0:0 1:0 2:0 3:0
total
I want to select the columns whose grand total is greater than 0. How to achieve it?
CREATE TABLE #tempi ( nav1 VARCHAR(50), nav2 INT, nav3 INT, nav4 INT, nav5 INT )
INSERT INTO #tempi VALUES('Grand Total:', 0.0, 1.0, 2.0, 3.0)
DECLARE #dynsql NVARCHAR(MAX)
SELECT #dynsql = ISNULL(#dynsql + ',','') + QUOTENAME(col) FROM #tempi
UNPIVOT(nav FOR col IN (nav2,nav3,nav4,nav5)) AS unpvt
WHERE nav1 = 'Grand Total:' AND nav>0
IF(##ROWCOUNT > 0)
BEGIN
SET #dynsql = 'SELECT ' + #dynsql + ' FROM #tempi'
EXEC(#dynsql)
END
DROP TABLE #tempi
Or if you can't use UNPIVOT...
DECLARE #dynsql NVARCHAR(MAX)
SELECT #dynsql = CASE WHEN nav2>0 THEN ',nav2' ELSE '' END +
CASE WHEN nav3>0 THEN ',nav3' ELSE '' END +
CASE WHEN nav4>0 THEN ',nav4' ELSE '' END +
CASE WHEN nav5>0 THEN ',nav5' ELSE '' END
FROM #tempi
WHERE nav1 = 'Grand Total:'
IF(LEN(#dynsql) > 0)
BEGIN
SET #dynsql = STUFF(#dynsql,1,1,'SELECT ') + ' FROM #tempi'
EXEC(#dynsql)
END