Related
It is a bit complicated query but let me elaborate,
i have got multiple filter parameters in my procedure,
also a view named Vw1 and its regarded my main object in query that most of the filter parameters are filtered against this view that is located in another server,then i need to compare this view to another table and check two sides columns for possible conflict and then check if there is any conflict.
you can see my query below
ALTER PROC [dbo].[spGetAyandehVagozariWithConflict]
#Take INT,
#Skip INT,
#FromVagozariDate NVARCHAR(10),
#ToVagozariDate NVARCHAR(10),
#OrderBankCode NVARCHAR(4)=null,
#OrderBrCode NVARCHAR(7)=null,
#BenefBrCode NVARCHAR(4)=null,
#FromAmount NVARCHAR(50)=null,
#ToAmount NVARCHAR(50)=null,
#CheqSerie NVARCHAR(50)=null,
#CheqSerial NVARCHAR(10)=null,
#CheqStatus NVARCHAR(4)=null,
#CreditorNationalCode VARCHAR(20)=NULL,
#CheqType NVARCHAR(4)=null,
#BenefAccn NVARCHAR(13)=null,
#FromMatDate NVARCHAR(10)=null,
#ToMatDate NVARCHAR(10)=null,
#FromTaeenVaziatDate NVARCHAR(10)=null,
#ToTaeenVaziatDate NVARCHAR(10)=NULL,
#HasConflict VARCHAR(4)=null,
#TotalCnt INT OUTPUT,
#SUM BIGINT OUTPUT
AS
BEGIN
DECLARE #Query NVARCHAR(MAX)=''
IF(NULLIF(#FromVagozariDate,'') IS null)
SET #FromVagozariDate='NULL'
IF(NULLIF(#ToVagozariDate,'') IS null)
SET #ToVagozariDate='NULL'
IF(NULLIF(#OrderBrCode,'') IS null)
SET #OrderBrCode='NULL'
IF(NULLIF(#OrderBankCode,'') IS null)
SET #OrderBankCode='NULL'
IF(NULLIF(#BenefBrCode,'') IS null)
SET #BenefBrCode='NULL'
IF(NULLIF(#FromAmount,'') IS null)
SET #FromAmount='NULL'
IF(NULLIF(#ToAmount,'') IS null)
SET #ToAmount='NULL'
IF(NULLIF(#CheqSerie,'') IS null)
SET #CheqSerie='NULL'
IF(NULLIF(#CheqSerial,'') IS null)
SET #CheqSerial='NULL'
IF(NULLIF(#CheqStatus,'') IS null)
SET #CheqStatus='NULL'
IF(NULLIF(#CheqType,'') IS null)
SET #CheqType='NULL'
IF(NULLIF(#BenefAccn,'') IS null)
SET #BenefAccn='NULL'
IF(NULLIF(#ToMatDate,'') IS null)
SET #ToMatDate='NULL'
IF(NULLIF(#FromMatDate,'') IS null)
SET #FromMatDate='NULL'
IF(NULLIF(#FromTaeenVaziatDate,'') IS null)
SET #FromTaeenVaziatDate='NULL'
IF(NULLIF(#ToTaeenVaziatDate,'') IS null)
SET #ToTaeenVaziatDate='NULL'
IF(NULLIF(#HasConflict,'') IS null)
SET #HasConflict='NULL'
IF(NULLIF(#CreditorNationalCode,'') IS null)
SET #CreditorNationalCode='NULL'
IF #FromVagozariDate<>'NULL'
BEGIN
SET #FromVagozariDate=REPLACE(#FromVagozariDate,'/','')
END
IF #ToVagozariDate<>'NULL'
BEGIN
SET #ToVagozariDate=REPLACE(#ToVagozariDate,'/','')
END
IF #FromMatDate<>'NULL'
BEGIN
SET #FromMatDate=REPLACE(#FromMatDate,'/','')
END
IF #ToMatDate<>'NULL'
BEGIN
SET #ToMatDate=REPLACE(#ToMatDate,'/','')
END
IF #FromTaeenVaziatDate<>'NULL'
BEGIN
SET #FromTaeenVaziatDate=REPLACE(#FromTaeenVaziatDate,'/','')
SET #FromTaeenVaziatDate=SUBSTRING(#FromTaeenVaziatDate,3,6)
END
IF #ToTaeenVaziatDate<>'NULL'
BEGIN
SET #ToTaeenVaziatDate=REPLACE(#ToTaeenVaziatDate,'/','')
SET #ToTaeenVaziatDate=SUBSTRING(#ToTaeenVaziatDate,3,6)
END
IF #OrderBankCode<>'NULL'
BEGIN
--SET #OrderBankCode='0'+#OrderBankCode;
SET #OrderBankCode=dbo.LeftPad(#OrderBankCode,'0',3)
END
IF(#BenefBrCode<>'NULL')
BEGIN
SET #BenefBrCode=dbo.LeftPad(#BenefBrCode,'0',4)
END
IF(#OrderBrCode<>'NULL')
BEGIN
SET #OrderBrCode=dbo.LeftPad(#OrderBrCode,'0',4)
END
SET #Query=#Query+N'
SELECT *
INTO #Vw1
FROM [LinkedServer].DbName.dbo.Vw1 vd
where (vd.mat_date BETWEEN '+''''+#FromMatDate+''''+' AND'+''''+ #ToMatDate+''''+' OR ('+''''+#FromMatDate+''''+' =''NULL'' AND '+''''+#ToMatDate+''''+' =''NULL'' )) AND
(dbo.LeftPad(vd.order_bank_code,''0'',3)= '+''''+#OrderBankCode+''''+' OR '+''''+#OrderBankCode+''''+' =''NULL'') AND
(dbo.LeftPad(vd.order_brcode,''0'',4)= '+''''+#OrderBrCode+''''+' OR '+''''+#OrderBrCode+''''+' =''NULL'') AND
(dbo.LeftPad(vd.Benef_br_code,''0'',4)= '+''''+#BenefBrCode+''''+' OR '+''''+#BenefBrCode+''''+' =''NULL'') AND
(substring(vd.amount, patindex(''%[^0]%'',vd.amount), 10) BETWEEN '+''''+#FromAmount+''''+' AND '+''''+#ToAmount+''''+' OR ('+''''+#FromAmount+''''+' =''NULL'' AND '+''''+#ToAmount+''''+' =''NULL'')) AND
(RTRIM(LTRIM(vd.chq_serie))= '+''''+#CheqSerie+''''+' OR '+''''+#CheqSerie+''''+' =''NULL'') AND
(RTRIM(LTRIM(vd.chq_serial))= '+''''+#CheqSerial+''''+' OR '+''''+#CheqSerial+''''+' =''NULL'') AND
(vd.Chq_stat= '+''''+#CheqStatus+''''+' OR '+''''+#CheqStatus+''''+' in (''NULL'',''-1'')) AND
(vd.chq_type= '+''''+#CheqType+''''+' OR '+''''+#CheqType+''''+' =''NULL'') AND
(vd.benef_acno= '+''''+#BenefAccn+''''+' OR '+''''+#BenefAccn+''''+' =''NULL'') AND
((vd.taeen_vaziat_date is null AND ('+''''+#CheqStatus+''''+'=''-1'')) or('+''''+#CheqStatus+''''+'<>''-1'') ) AND
(vd.vagozari_date BETWEEN '+''''+#FromVagozariDate+''''+' AND '+''''+#ToVagozariDate+''''+' OR ('+''''+#FromVagozariDate+''''+' =''NULL'' AND '+''''+#ToVagozariDate+''''+' =''NULL'')) AND
(vd.taeen_vaziat_date BETWEEN '+''''+#FromTaeenVaziatDate+''''+' AND '+''''+#ToTaeenVaziatDate+''''+' OR ('+''''+#ToTaeenVaziatDate+''''+' =''NULL'' AND '+''''+#FromTaeenVaziatDate+''''+' =''NULL''))
order by system_no
select distinct cast(benef_acno as bigint) benef_acno
into #acno
from #Vw1
select ACNO,CUSTNO
into #ACTINFO
from [LinkServer1].[Db1].[dbo].[ACTINFO] af
where accno_id in (select * from #acno)
select [ECONOMIC-CODE],CUSTNO,FIRSTNAME +'' ''+LASTNAME CUSTNAME
into #CUSTINFO
from [LinkServer1].[Db1].[dbo].[CUSTINFO]
where CustNo_Id in (select cast(CUSTNO as bigint) from #ACTINFO)
SELECT
vd.*,
ct.Title CheqTypeTitle,
af.CUSTNO,cf.CUSTNAME,
--'''' as CUSTNO,'''' CUSTNAME,
cs.Status CheqStatusTitle,
av.Id AyandehVagozariId,
p.ParticipantName BenefBankName,
CASE WHEN ISNULL(vd.vagozari_date,'''') <> COALESCE(REPLACE(av.VagozariDate,''/'',''''),vd.vagozari_date,'''') THEN 1 ELSE 0 END VagozariDateHasConflict,
CASE WHEN ISNULL(dbo.LeftPad(vd.order_bank_code,''0'',3),'''') <> COALESCE(av.Order_Bank_Code,dbo.LeftPad(vd.order_bank_code,''0'',3),'''') THEN 1 ELSE 0 END OrderBankCodeHasConflict,
CASE WHEN ISNULL(dbo.LeftPad(vd.order_brcode,''0'',4),'''') <> COALESCE(av.Order_brCode,dbo.LeftPad(vd.order_brcode,''0'',4),'''') THEN 1 ELSE 0 END OrderBrCodeHasConflict,
CASE WHEN ISNULL(dbo.LeftPad(vd.Benef_br_code,''0'',4),'''') <> COALESCE(av.Benef_Br_Code,dbo.LeftPad(vd.Benef_br_code,''0'',4),'''') THEN 1 ELSE 0 END BenefBrCodeHasConflict,
CASE WHEN ISNULL(substring(vd.amount, patindex(''%[^0]%'',vd.amount), 10),'''') <> COALESCE(av.Amount,substring(vd.amount, patindex(''%[^0]%'',vd.amount), 10),'''') THEN 1 ELSE 0 END AmountHasConflict,
CASE WHEN ISNULL(vd.chq_serie,'''') <> COALESCE(av.Chq_serie,vd.chq_serie,'''') THEN 1 ELSE 0 END CheqSerieHasConflict,
CASE WHEN ISNULL(RTRIM(LTRIM(vd.chq_serial)),'''') <> COALESCE(av.Chq_Serial,RTRIM(LTRIM(vd.chq_serial)),'''') THEN 1 ELSE 0 END CheqSerialHasConflict,
CASE WHEN ISNULL(vd.chq_type,'''') <> COALESCE(av.Chq_Type,vd.chq_type,'''') THEN 1 ELSE 0 END CheqTypeHasConflict,
CASE WHEN ISNULL(vd.benef_acno,'''') <> COALESCE(av.Benef_Acno,vd.benef_acno,'''') THEN 1 ELSE 0 END BenefAcnoHasConflict,
CASE WHEN ISNULL(vd.mat_date,'''') <> COALESCE(REPLACE(av.Mat_Date,''/'',''''),vd.mat_date,'''') THEN 1 ELSE 0 END MatDateHasConflict,
CASE WHEN ISNULL(cf.CUSTNO,'''') <> COALESCE(av.CUSTNO,cf.CUSTNO COLLATE DATABASE_DEFAULT,'''') THEN 1 ELSE 0 END CustNoHasConflict,
--CASE WHEN ISNULL(vd.taeen_vaziat_date,'''') <> COALESCE(av.Taeen_Vaziat_Date,vd.taeen_vaziat_date,'''') THEN 1 ELSE 0 END TaeenVaziatDateHasConflict,
0 as TaeenVaziatDateHasConflict,
CASE WHEN ISNULL(vd.Chq_stat,'''') <> COALESCE(av.Chq_Status,vd.Chq_stat,'''') THEN 1 ELSE 0 END CheqStatusHasConflict
--0 as VagozariDateHasConflict,
--0 as OrderBankCodeHasConflict,
--0 as OrderBrCodeHasConflict,
--0 as BenefBrCodeHasConflict,
--0 as AmountHasConflict,
--0 as CheqSerieHasConflict,
--0 as CheqSerialHasConflict,
--0 as CheqTypeHasConflict,
--0 as BenefAcnoHasConflict,
--0 as MatDateHasConflict,
--0 as TaeenVaziatDateHasConflict
INTO #temp
FROM #vw_faranam_vagozari_chakavak vd
LEFT JOIN #ACTINFO af on vd.benef_acno=af.ACNO COLLATE Arabic_CI_AS
LEFT JOIN #CUSTINFO cf on af.CUSTNO=cf.CUSTNO COLLATE Arabic_CI_AS
LEFT JOIN [AyandehVagozari] av ON av.SystemNo=vd.system_no
LEFT JOIN [Cheque_Type_Biha] ct ON ct.Code=vd.chq_type
LEFT JOIN [ChequeStatusForBiha] cs ON cs.Id=vd.Chq_stat
LEFT JOIN [Participant] p ON ''0''+p.ParticipantCode=vd.order_bank_code
WHERE (cf.[ECONOMIC-CODE]= '+''''+#CreditorNationalCode+''''+' OR '+''''+#CreditorNationalCode+''''+' =''NULL'')
order by vd.system_no
DELETE t FROM(
SELECT *,ROW_NUMBER() OVER(partition by system_no order by AyandehVagozariId desc) rn FROM
#temp
)t
where rn>1
SELECT *,
case when (VagozariDateHasConflict=1 or OrderBankCodeHasConflict=1 or OrderBrCodeHasConflict =1 OR BenefBrCodeHasConflict=1
or AmountHasConflict=1 or CheqSerieHasConflict=1 or CheqSerialHasConflict=1 or CustNoHasConflict=1
or CheqStatusHasConflict=1 or CheqTypeHasConflict=1 or BenefAcnoHasConflict=1 or MatDateHasConflict=1 or TaeenVaziatDateHasConflict=1)
then 1 else 0 end HasConflict
INTO #TmpResult
FROM #temp
order by system_no
--here is where i filter my last parameter but it seems too late..because alot of records already involved !
SELECT *,ROW_NUMBER() over (ORDER BY system_no) RowNumber
into #finalTemp
FROM
#TmpResult
where (HasConflict=1 and '+''''+#HasConflict+''''+'=''1'')
OR (HasConflict=0 and '+''''+#HasConflict+''''+'=''0'')
OR ('+''''+#HasConflict+''''+'=''NULL'')
order by system_no
SELECT #Total=##ROWCOUNT
select #Sum=isnull(SUM(cast(amount as bigint)),0) from
#finalTemp
select * from
#finalTemp
WHERE RowNumber >'+CAST(#Skip AS NVARCHAR(10))+' AND RowNumber<=('+CAST(#Skip+#Take AS NVARCHAR(10))+')
'
PRINT #Query
EXEC sp_executesql #Query,N'#Total int output,#Sum bigint output',#TotalCnt OUTPUT,#SUM OUTPUT
END
due to performance issues i had to write it in dynamic way,But the problem is that i could not wrap my head around paging in this query, bacause as you see i should put paging line in the end of query because #HasConflict parameters exist and i cannot check it earlier,and in this way my paging seems pointless because i'm dealing with huge load of data ...
also i cannot use offset for paging because my sql version is too old ...
I have 2 stored procedures which return the same columns that I am trying to merge into a single procedure. They both have a different set of parameters and both have different WHERE clauses, but they use the same tables and select the exact same rows.
WHERE clause 1: (uses #UIOID, and #Level)
WHERE ( #UIOID = CASE WHEN #Level = 'Single' THEN C.C_UIOID_PK
WHEN #Level = 'Children' THEN CLC.UIOL_P
WHEN #Level = 'Parent' THEN CLP.UIOL_C
END
OR ( #UIOID = '0'
AND #Level = 'All'
)
)
Where clause 2: (Uses #TeamCode, #Year, #IncludeQCodes)
WHERE C.C_IsChild = 0
AND C.C_MOA <> 'ADD'
AND #TeamCode = C.C_OffOrg
AND C.C_Active = 'Y'
AND ( #Year BETWEEN dbo.f_GetAcYearByDate(C.C_StartDate) AND dbo.f_GetAcYearByDate(C.C_EndDate)
OR #Year = 0 )
AND ( C.C_InstCode NOT LIKE 'Q%'
OR #IncludeQCodes = 1 )
Ideally I want to add a new parameter which basically tells it which of the two WHERE clauses to run, but I can't seem to recreate that with CASE statement because as far as I can tell, they only work for a single WHERE clause, not a whole set of different clauses
I want to do this without having to repeat the select statement again and putting the whole thing in IF statements, and i don't want to put the query into a string either. I just want one select statement ideally.
The problem with using temp tables is the query itself takes a while to run without any parameters and is used in a live website, so I don't want it to have to put all records in a temp table and then filter it.
The problem with using a CTE is you can't follow it with an IF statement, so that wouldn't work either.
Here is the sort of logic I am trying to achieve:
SELECT A
B
C
FROM X
IF #WhichOption = 1 THEN
WHERE ( #UIOID = CASE WHEN #Level = 'Single' THEN C.C_UIOID_PK
WHEN #Level = 'Children' THEN CLC.UIOL_P
WHEN #Level = 'Parent' THEN CLP.UIOL_C
END
OR ( #UIOID = '0'
AND #Level = 'All'
)
)
ELSE IF #WhichOption = 2 THEN
WHERE C.C_IsChild = 0
AND C.C_MOA <> 'ADD'
AND #TeamCode = C.C_OffOrg
AND C.C_Active = 'Y'
AND ( #Year BETWEEN dbo.f_GetAcYearByDate(C.C_StartDate) AND dbo.f_GetAcYearByDate(C.C_EndDate)
OR #Year = 0 )
AND ( C.C_InstCode NOT LIKE 'Q%'
OR #IncludeQCodes = 1 )
Save the following process in a procedure. You can also directly insert into a physical table.
declare #varTable Table (columns exactly as Procedures return)
if(condition is met)
begin
insert into #varTable
exec proc1
end
else
begin
insert into #varTable
exec proc2
end
Add the parameter that you said that it would indicate what filter apply :
select XXXXX
from XXXXX
where (#Mode = 1 and ( filter 1 ))
or
(#Mode = 2 and ( filter 2 ))
option(recompile)
If the #Mode parameter is 1 then it will evaluate the filter 1, otherwise it will evaluate the filter 2.
Add an option(recompile) at the end of the statement, so the SQL engine will replace the variables with their values, eliminate the filter that won't be evaluated, and generate an execution plant for just the filter that you want to apply.
PS: Please notice that although these catchall queries are very easy to code and maintain, and generate a perfectly functional and optimal execution, they are not advised for high-demand applications. The option(recompile) forces the engine to recompile and generate a new execution plan at every execution and that would have a noticeable effect on performance if your query needs to be executed hundreds of times per minute. But for the occasional use it's perfectly fine.
Try to use dynamic SQL:
DECLARE #sql NVARCHAR(max), #where NVARCHAR(max), #WhichOption INT = 1;
SET #sql = 'SELECT A
B
C
FROM X';
IF #WhichOption = 1
SET #where = 'WHERE ( #UIOID = CASE WHEN #Level = ''Single'' THEN C.C_UIOID_PK
WHEN #Level = ''Children'' THEN CLC.UIOL_P
WHEN #Level = ''Parent'' THEN CLP.UIOL_C
END
OR ( #UIOID = ''0''
AND #Level = ''All''
)
)';
ELSE IF #WhichOption = 2
SET #where = ' WHERE C.C_IsChild = 0
AND C.C_MOA <> ''ADD''
AND #TeamCode = C.C_OffOrg
AND C.C_Active = ''Y''
AND ( #Year BETWEEN dbo.f_GetAcYearByDate(C.C_StartDate)
AND dbo.f_GetAcYearByDate(C.C_EndDate)
OR #Year = 0 )
AND ( C.C_InstCode NOT LIKE ''Q%''
OR #IncludeQCodes = 1 ) ';
SET #sql = CONCAT(#sql,' ', #where)
PRINT #sql
EXECUTE sp_executesql #sql
I have 2 set of values in a column i.e first 4 character are characters and next 4 character are numeric.
Ex:AAAA1234
Now I have to increment the value from right end i.e when numeric value reached 9999 then I have to increment character by 1 character.
Sample :
Consider the last value stored in a column is AAAA9999 then next incremented values should be in a sequence AAAB9999,....... AABZ9999,..... BZZZ9999..... ZZZZ9999(last value). And when it reaches ZZZZ9999 then I have to reset the value to AAAA0001.
How can do it in T-SQL ???
Here is a conceptual script, which does what you want. You will need to tweak it to suit your requirements
DECLARE #test table(TestValue char(8))
DECLARE #CharPart char(4),#NumPart int
SET #CharPart = 'AAAA'
SET #NumPart = 1
WHILE #NumPart <=9999
BEGIN
INSERT INTO #test
SELECT #CharPart+RIGHT(('0000'+CAST(#NumPart AS varchar(4))),4)
IF #NumPart = 9999
BEGIN
IF SUBSTRING(#CharPart,4,1)<>'Z'
BEGIN
SET #CharPart = LEFT(#CharPart,3)+CHAR(ASCII(SUBSTRING(#CharPart,4,1))+1)
SET #NumPart = 1
END
ELSE IF SUBSTRING(#CharPart,4,1)='Z' AND SUBSTRING(#CharPart,3,1) <>'Z'
BEGIN
SET #CharPart = LEFT(#CharPart,2)+CHAR(ASCII(SUBSTRING(#CharPart,3,1))+1)+RIGHT(#CharPart,1)
SET #NumPart = 1
END
ELSE IF SUBSTRING(#CharPart,3,1)='Z' AND SUBSTRING(#CharPart,2,1) <>'Z'
BEGIN
SET #CharPart = LEFT(#CharPart,1)+CHAR(ASCII(SUBSTRING(#CharPart,2,1))+1)+RIGHT(#CharPart,2)
SET #NumPart = 1
END
ELSE IF SUBSTRING(#CharPart,1,1)<>'Z'
BEGIN
SET #CharPart = CHAR(ASCII(SUBSTRING(#CharPart,1,1))+1)+RIGHT(#CharPart,3)
SET #NumPart = 1
END
ELSE IF SUBSTRING(#CharPart,1,1)='Z'
BEGIN
SET #CharPart = 'AAAA'
SET #NumPart = 1
INSERT INTO #test
SELECT #CharPart+RIGHT(('0000'+CAST(#NumPart AS varchar(4))),4)
BREAK
END
END
ELSE
BEGIN
SET #NumPart=#NumPart+1
END
END
SELECT * FROM #test
With the help of PATINDEX,SUBSTRING,ASCII functions you can achieve your special cases.
(I have found the solution for your special cases). Likewise you can add your own addition feature.
create table #temp(col1 varchar(20))
insert into #temp values('AAAA9999')
insert into #temp values('AAAZ9999')
insert into #temp values('AAZZ9999')
insert into #temp values('AZZZ9999')
insert into #temp values('ZZZZ9999')
select * from #temp
select col1,
case when cast(substring(col1,patindex('%[0-9]%',col1),len(col1)) as int) = 9999 and left(col1,4) <> 'ZZZZ'
then
case
when substring(col1,(patindex('%[0-9]%',col1)-1),1) <> 'Z' then left(col1,3)+char(ASCII(substring(col1,(patindex('%[0-9]%',col1)-1),1)) + 1)+right(col1,4)
when substring(col1,(patindex('%[0-9]%',col1)-2),1) <> 'Z' then left(col1,2)+char(ASCII(substring(col1,(patindex('%[0-9]%',col1)-2),1)) + 1)+right(col1,5)
when substring(col1,(patindex('%[0-9]%',col1)-3),1) <> 'Z' then left(col1,1)+char(ASCII(substring(col1,(patindex('%[0-9]%',col1)-3),1)) + 1)+right(col1,6)
when substring(col1,(patindex('%[0-9]%',col1)-4),1) <> 'Z' then char(ASCII(substring(col1,(patindex('%[0-9]%',col1)-4),1)) + 1)+right(col1,7)
end
else 'AAAA0001'
end as outputofcol1
--patindex('%[0-9]%',col1)-1 as charpos,
--substring(col1,(patindex('%[0-9]%',col1)-1),1) as substr4,
--substring(col1,(patindex('%[0-9]%',col1)-2),1) as substr3,
--substring(col1,(patindex('%[0-9]%',col1)-3),1) as substr2,
--substring(col1,(patindex('%[0-9]%',col1)-4),1) as substr1
--ASCII(substring(col1,(patindex('%[0-9]%',col1)-1),1)) as ASC_value
from #temp
The following function should return the desired value:
IF OBJECT_ID (N'dbo.ufnGetIndexValue') IS NOT NULL
DROP FUNCTION dbo.ufnGetIndexValue;
GO
CREATE FUNCTION dbo.ufnGetIndexValue(#MainString CHAR(8))
RETURNS CHAR(8)
AS
BEGIN
DECLARE #NumberPart INT
DECLARE #StringPart CHAR(4)
DECLARE #Position TINYINT
DECLARE #char CHAR
SET #NumberPart=CONVERT(INT,SUBSTRING(#MainString,5,8))
SET #StringPart=SUBSTRING(#MainString,1,4)
IF #NumberPart=9999
BEGIN
SET #NumberPart=1111;
SET #Position=4
WHILE #Position >= 1
BEGIN
SET #char=SUBSTRING(#StringPart,#Position,1)
IF(#char!='Z')
BEGIN
SET #char=CHAR(ASCII(#char)+1);
SET #StringPart = STUFF(#StringPart,#Position,1,#char);
BREAK;
END
SET #StringPart = STUFF(#StringPart,#Position,1,'A');
SET #Position-=1;
END
END
ELSE
BEGIN
SET #NumberPart+=1;
END
SET #MainString=#StringPart+CAST(#NumberPart AS CHAR(4));
RETURN #MainString
END
GO
Here is a scalar select function that do the increment.
CREATE FUNCTION dbo.inc_serial( #id char(8) )
RETURNS char(8) BEGIN
select #id = case when SUBSTRING(id,2,1) <> '[' then id else STUFF( id, 1, 2, char(((ascii(id)+1-65)%26)+65) + 'A' ) end from (
select case when SUBSTRING(id,3,1) <> '[' then id else STUFF( id, 2, 2, char(ascii(right(id,7))+1) + 'A' ) end as id from (
select case when SUBSTRING(id,4,1) <> '[' then id else STUFF( id, 3, 2, char(ascii(right(id,6))+1) + 'A' ) end as id from (
select
case when right(#id,4) < '9999'
then concat( left(#id,4), right(concat( '000', (cast(right(#id,4) as smallint)+1) ), 4 ) )
else concat( left(#id,3), char(ascii(right(#id,5))+1), '0001' ) end as id
) t1 ) t2 ) t3
RETURN #id
END
Basically, the code just add one to the number, and repeatingly carring overflow up to the left.
If your table always has one and only one row to be updated (e.g. an option/flag table):
UPDATE [table] SET [serial] = dbo.inc_serial( [serial] );
If your table has multiple rows, you will need an identity or high precision creation time column, so that we know where to continue from after reset.
INSERT INTO [table] (serial) VALUES ( dbo.inc_serial((
select top 1 case when count(*) > 0 then max([serial]) else 'AAAA0000' end AS id
from [table] where [id] = ( select max([id]) from [table] )
)));
For concurrency safety, use XLOCK,ROWLOCK,HOLDLOCK to lock the table.
They are obmitted from the examples for simplicity.
If you do not like udf, you can embedded the query inline.
An inline example for first case:
UPDATE [table] SET [serial] = ((
select case when SUBSTRING(id,2,1) <> '[' then id else STUFF( id, 1, 2, char(((ascii(id)+1-65)%26)+65) + 'A' ) end as id from (
select case when SUBSTRING(id,3,1) <> '[' then id else STUFF( id, 2, 2, char(ascii(right(id,7))+1) + 'A' ) end as id from (
select case when SUBSTRING(id,4,1) <> '[' then id else STUFF( id, 3, 2, char(ascii(right(id,6))+1) + 'A' ) end as id from (
select
case when right(id,4) < '9999'
then concat( left(id,4), right(concat( '000', (cast(right(id,4) as smallint)+1) ), 4 ) )
else concat( left(id,3), char(ascii(right(id,5))+1), '0001' ) end as id
from (
select top 1 [serial] as id from [table] with (XLOCK,ROWLOCK,HOLDLOCK)
) t0
) t1 ) t2 ) t3
))
The function can also be written as an inline table value function for better performance, at cost of more complex usage, but I would not border unless it frequently runs on multiple rows.
SQL is not my best thing but I have been trying to optimize this stored procedure. It had multiple scalar-valued functions that I tried to change to table-valued functions because I read in many places that it's a more efficient way of doing it. And now I have them made but not real sure how to implement or if I maybe just didn't create them correctly.
This is the function I'm calling.
Alter FUNCTION [IsNotSenateActivityTableValue]
(
#ActivityCode int,
#BillId int,
#TextToDisplay varchar(max)
)
returns #T table(result varchar(max))
as
begin
DECLARE #result varchar(max);
declare #countcodes int;
declare #ishousebill int;
select #ishousebill = count(billid)
from BillMaster
where BillID = #BillID and Chamber = 'H'
If (#ishousebill = 0)
begin
SELECT #countcodes = count([ActivityCode])
FROM [HouseCoreData].[dbo].[ActivityCode]
where ActivityDescription not like '%(H)%' and ActivityType = 'S'
and [ActivityCode] = #ActivityCode
if (#countcodes = 0)
begin
set #result = 'test'
end
else
begin
set #result = 'test2'
end
end
else
begin
set #result = #TextToDisplay
end
RETURN
END
And this is how I was trying to call them like this. I would prefer just being able to put them in the top but really anything that works would be good.
SELECT distinct
ActionDates.result as ActionDate
,ActivityDescriptions.result as ActivityDescription
FROM BillWebReporting.vwBillDetailWithSubjectIndex as vw
left outer join [BillWebReporting].[HasHouseSummary] as HasSummary on vw.BillID = HasSummary.BillID
outer APPLY dbo.IsNotSenateActivityDateTableValue(ActivityCode,vw.BillID,[ActionDate]) ActionDates
OUTER APPLY dbo.IsNotSenateActivityTableValue(ActivityCode,vw.BillID,[ActivityDescription]) as ActivityDescriptions
Getting a count just to see if at least one row exists is very expensive. You should use EXISTS instead, which can potentially short circuit without materializing the entire count.
Here is a more efficient way using an inline table-valued function instead of a multi-statement table-valued function.
ALTER FUNCTION dbo.[IsNotSenateActivityTableValue] -- always use schema prefix!
(
#ActivityCode int,
#BillId int,
#TextToDisplay varchar(max)
)
RETURNS TABLE
AS
RETURN (SELECT result = CASE WHEN EXISTS
(SELECT 1 FROM dbo.BillMaster
WHERE BillID = #BillID AND Chamber = 'H'
) THEN #TextToDisplay ELSE CASE WHEN EXISTS
(SELECT 1 FROM [HouseCoreData].[dbo].[ActivityCode]
where ActivityDescription not like '%(H)%'
and ActivityType = 'S'
and [ActivityCode] = #ActivityCode
) THEN 'test2' ELSE 'test' END
END);
GO
Of course it could also just be a scalar UDF...
ALTER FUNCTION dbo.[IsNotSenateActivityScalar] -- always use schema prefix!
(
#ActivityCode int,
#BillId int,
#TextToDisplay varchar(max)
)
RETURNS VARCHAR(MAX)
AS
BEGIN
DECLARE #result VARCHAR(MAX);
SELECT #result = CASE WHEN EXISTS
(SELECT 1 FROM dbo.BillMaster
WHERE BillID = #BillID AND Chamber = 'H'
) THEN #TextToDisplay ELSE CASE WHEN EXISTS
(SELECT 1 FROM [HouseCoreData].[dbo].[ActivityCode]
where ActivityDescription not like '%(H)%'
and ActivityType = 'S'
and [ActivityCode] = #ActivityCode
) THEN 'test2' ELSE 'test' END
END;
RETURN (#result);
END
GO
Table-valued functions return a table, in which, like any other table, rows have to be inserted.
Instead of doing set #result = ....., do:
INSERT INTO #T (result) VALUES ( ..... )
EDIT: As a side note, I don't really understand the reason for this function to be table-valued. You are essentially returning one value.
First of all UDFs generally are very non-performant. I am not sure about MySQL, but in Sql Server a UDF is recompiled every time (FOR EACH ROW OF OUTPUT) it is executed, except for what are called inline UDFs, which only have a single select statement, which is folded into the SQL of the outer query it is included in... and so is only compiled once.
MySQL does have inline table-valued functions, use it instead... in SQL Server, the syntax would be:
CREATE FUNCTION IsNotSenateActivityTableValue
(
#ActivityCode int,
#BillId int,
#TextToDisplay varchar(max)
)
RETURNS TABLE
AS
RETURN
(
Select case
When y.bilCnt + z.actCnt = 0 Then 'test'
when y.bilCnt = 0 then 'test2'
else #TextToDisplay end result
From (Select Count(billId) bilCnt
From BillMaster
Where BillID = #BillID
And Chamber = 'H') y
Full Join
(Select count([ActivityCode]) actCnt
From [HouseCoreData].[dbo].[ActivityCode]
Where ActivityDescription not like '%(H)%'
And ActivityType = 'S'
And [ActivityCode] = #ActivityCode) z
)
GO
I'd like to assign some variables inside a query that uses CASE statements for it's columns. Not quite sure how to do this, having trouble finding the right syntax.
This is what I have so far, but it's got syntax errors.
-- set #theID and #theName with their appropriate values
select top (1)
#theID = (Case when B.ID IS NULL then A.ID else B.ID END) ,
#theName = (Case when B.Name IS NULL then A.Name else B.Name END)
from B left join A on A.ID = B.ID where ...
What's the correct place/way to stick those variables in there?
The example you've given should work. You can assign to variables from a case statement. Just pretend that the entire CASE..WHEN..THEN..ELSE..END block is a field. Here is a generic example:
declare #string1 nvarchar(100) = null
declare #string2 nvarchar(100) = null
select
#string1 = case when 1=1 then 'yes' else 'no' end
,#string2 = case when 1=0 then 'yes' else 'no' end
print 'string1 = ' + #string1
print 'string2 = ' + #string2
Gives:
string1 = yes
string2 = no
Can you tell us what specific error(s) you are getting?
You could probably do this more easily using ISNULL or COALESCE:
select top (1)
#theID = ISNULL(B.ID, A.ID),
#theName = ISNULL(B.Name, A.Name),
from B left join A on A.ID = B.ID where ...
DECLARE #SmallBlindSeatId INT
DECLARE #BigBlindSeatId INT
DECLARE #DealerSeatId INT
DECLARE #NextTurn INT
SELECT #DealerSeatId=( CASE WHEN BlindsInfo=1 THEN SeatId ELSE #DealerSeatId END ),
#SmallBlindSeatId=( CASE WHEN BlindsInfo=2 THEN SeatId ELSE #SmallBlindSeatId END),
#BigBlindSeatId=( CASE WHEN BlindsInfo=3 THEN SeatId ELSE #BigBlindSeatId END),
#NextTurn=( CASE WHEN NEXTTURN=1 THEN SeatId ELSE #NextTurn END)
FROM ABC WHERE TESTCASEID=1
PRINT(#DealerSeatId)
PRINT(#SmallBlindSeatId)
PRINT(#BigBlindSeatId)
PRINT (#NextTurn)