I have a question about the parametrization of a SQL query I created. The original query looks like this:
DECLARE #portfolios AS NVARCHAR(MAX)
DECLARE #Abfrage AS NVARCHAR(MAX)
SET #portfolios = STUFF((SELECT '[' + PortfolioName + '], ' FROM tblPortfolio ORDER BY PortfolioName FOR XML PATH('')),1,5,NULL)
SELECT #portfolios = LEFT(#portfolios,(LEN(#portfolios)-1))
SET #Abfrage ='
SELECT Description, ' + #portfolios + '
FROM
( SELECT tblReturnType.Description, tblPortfolio.PortfolioName, tblReturnAbs.Value
FROM
tblReturnType INNER JOIN ((tblReturnAbs INNER JOIN tblPortfolio ON tblReturnAbs.PortfolioNo = tblPortfolio.PortfolioNo) INNER JOIN tblInstrument ON tblReturnAbs.InstrumentNo =tblInstrument.InstrumentNo) ON (tblReturnType.ReturnType = tblReturnAbs.ReturnType)
WHERE (tblReturnAbs.Date BETWEEN ''01/01/17'' AND Dateadd("d",-1,''01/31/17'')) AND tblInstrument.Currency = ''EUR''
) AS x
PIVOT
(
sum(x.Value)
For x.PortfolioName in (' + #portfolios + ')
) AS piv
ORDER BY Description
'
Execute (#Abfrage)
It creates an output where a variable number of portfolios is displayed as columns, with different parts of portfolio earnings displayed under each portfolio name. Now, i would like to use parameters to be able to dynamically change the "WHERE" clause. At the same time, it should be possible to access the query from a frontend (which is yet to be decided). To achieve this, i tried to transform the query in a stored procedure like this:
ALTER PROCEDURE [dbo].[Return_Attribution]
#Startdate datetime,
#EndDate datetime,
#Currency nvarchar(10)
AS
BEGIN
DECLARE #portfolios AS NVARCHAR(MAX)
DECLARE #Abfrage AS NVARCHAR(MAX)
SET #portfolios = STUFF((SELECT '[' + PortfolioName + '], ' FROM tblPortfolio ORDER BY PortfolioName FOR XML PATH('')),1,5,NULL)
SELECT #portfolios = LEFT(#portfolios,(LEN(#portfolios)-1))
SET #Abfrage ='SELECT Description, ' + #portfolios + '
FROM
(SELECT tblReturnType.Description, tblPortfolio.PortfolioName, tblReturnAbs.Value
FROM tblReturnType
INNER JOIN ((tblReturnAbs
INNER JOIN tblPortfolio ON tblReturnAbs.PortfolioNo = tblPortfolio.PortfolioNo)
INNER JOIN tblInstrument ON tblReturnAbs.InstrumentNo =tblInstrument.InstrumentNo) ON (tblReturnType.ReturnType = tblReturnAbs.ReturnType)
WHERE (tblReturnAbs.Date >= '+ #Startdate +' AND tblReturnAbs.Date <= Dateadd("d",-1,'+ #EndDate +')) AND tblInstrument.Currency = '+ #Currency +'
) AS x
PIVOT
(
sum(x.Value)
For x.PortfolioName in (' + #portfolios + ')
) AS piv
ORDER BY Description
'
Execute (#Abfrage)
END
Now, if I try to execute this procedure and enter parameters, I get the following error:
Msg 8152, Level 16, State 10, Procedure Return_Attribution, Line 16
String or binary data would be truncated.
I did a bit of research and I found out that this error normally occurs when a string is too long to fit into a data field. As it only occurs when I used the parametrized procedure, I suppose I made a mistake in either defining or using the parameters. Any help would be appreciated.
Related
I have a query that inside in quotes:
DECLARE #PivotQuery AS NVARCHAR(MAX),
#PivotColumns AS NVARCHAR(MAX),
#PivotNull AS NVARCHAR(MAX)
SELECT #PivotColumns= ISNULL(#PivotColumns + ',','') + QUOTENAME(DepId)
FROM (SELECT DISTINCT sp.DepId FROM SKDEP sp) AS Stock
SELECT #PivotNull
= ISNULL(#PivotNull + ',','')
+ 'ISNULL(' + QUOTENAME(DepId) + ', 0) AS '
+ QUOTENAME(DepId)
FROM (SELECT DISTINCT sp.DepId FROM SKDEP sp) AS Stock
SET #PivotQuery =
N'SELECT P.P, P.D, '+#PivotNull+'
FROM
(
select
s.prdid as P,
p.prddsc as D,
case when mstes=''S'' then sum(-MstCntDisp) else sum(MstCntDisp) end as Stock,
s.DepId
from SKMOV S
join PRODUC P on s.PrdId=p.PrdId
where s.mstsal>0 AND s.MstTpo=''S'' and s.PrdId=**''*TF033''**
group by s.prdid, p.PrdDsc, s.MstES, s.depid
) AS t
PIVOT(sum(Stock)
FOR DEPID IN (' + #PivotColumns + ')) AS P'
EXEC sp_executesql #PivotQuery
In the where clause I need specific my parameters with the question mark, so:
and s.PrdId=?
But I don't know how to specify the parameter when having the query in quotes, because it doesn't take it.
Finally i resolved my problem that this way:
In the where clause, im pass value of one string,
where s.mstsal>0 AND s.MstTpo=''S'' and s.PrdId=''PRODUCT_ID''
And after when report open (in tab Script/beforeOpen), im replace the string value with the parameter.
this.queryText=this.queryText.replace("PRODUCT_ID",params["ID"].value);
And works!
I am developing a custom application using CodeIgniter and MSSQL Server. Here i am using stored procedures.
Now i am wondering to implement codeigniter query type functionality where i can create a universal stored procedure in SQL Server and at the time of using i can pass tablename, array of fields and values.
It can work for both insert and update.
Something like we do in CodeIgniter to execute the query,
$data = array('fieldname1' => 'value1',
'fieldname2' => 'value2');
$this->db->insert($tablename,$data);
Just like this if we can pass the table name and array of the data to stored procedure and stored procedure automatically execute it.
If this can be done, it can save lots n lots of man hours. If anyone have already done i will be very much happy to see the solution.
You need to make string very specific in this case.
Figure out your table name, Column name, Column values for insert. For update 2 more parameters are required Id column name and its value.
GO
---- exec InsertUpdate 'tablename', 'col1, col2, col3', 'val1, val2, val3', 'idcol', 'idval'
GO
Create proc InsertUpdate
( #TableName nvarchar(500),
#ColName nvarchar(max),
#ColValues nvarchar(max),
#IDColName nvarchar(100) = '', --- for update only otherwise null
#IdColValue nvarchar(Max) = '' --- for update only otherwise null
)
As
Begin
declare #Query nvarchar(max)
if (#IdColValue = '')
Begin
set #Query = ' Insert into ' + #TableName + ' (' + #ColName + ') values (' + #ColValues + ')'
End
Else
Begin
;with CtColumn as (
select ROW_NUMBER() over (order by (select 1000)) as Slno, * from Split(#ColName,',') )
, CtValue as (
select ROW_NUMBER() over (order by (select 1000)) as Slno, * from Split(#ColValues, ','))
, CTFinal as (
select CCOl.Slno, CCOl.Items as ColName, CVal.Items as ColValue from CtColumn as CCOl inner join CtValue as CVal on CCOl.Slno=CVal.Slno )
select #Query = 'update ' + #TableName + ' set ' +
stuff ( (select ',' + ColName + '=' + ColValue from CTFinal for xml path ('')) ,1,1,'') +
' where ' + #IDColName + '=' + #IdColValue
End
exec sp_executesql #Query
End
Go
I have to write update using dynamic sql becaus i know only name of column that I want to update and names of columns which I will use to join tables in my update. But I don't know the numbers of tables and names. Names of tables I will get in parameter of my procedure in this way
declare #Tables = N'Customer,Employee,Owner'
So I want to have update like this:
update t
set [Status] = 100
from
TemporaryTable t
left join Customer t1 on t1.RecordId = t.RecordId
left join Employee t2 on t2.RecordId = t.RecordId
left join Owner t3 on t3.RecordId =t.RecordId
where
t1.RecordId is null
and t2.RecordId is NULL
and t3.RecordId is null
I know that each table will have column RecordId and want to left join this tables to my TemporaryTable on this column but I don't know the names and numbers of tables. For example I will have one, two, or ten tables with different names. I know that this tables names will be save in parameter #Tables in that way:
#Tables = N'Customer,Employee,Owner'
There is possilble to write this update in dynamic way?
This is an answer, which helps ... to write update using dynamic sql ... and only shows how to generate a dynamic statement. It's based on string splitting. From SQL Server 2016+ you may use STRING_SPLIT() (because here the order of the substrings is not important). For previous versions you need to find a string splitting function.
T-SQL:
DECLARE #Tables nvarchar(max) = N'Customer,Employee,Owner'
DECLARE #join nvarchar(max) = N''
DECLARE #where nvarchar(max) = N''
DECLARE #stm nvarchar(max) = N''
SELECT
#join = #join + CONCAT(
N' LEFT JOIN ',
QUOTENAME(s.[value]),
N' t',
ROW_NUMBER() OVER (ORDER BY (SELECT 1)),
N' ON t',
ROW_NUMBER() OVER (ORDER BY (SELECT 1)),
N'.RecordId = t.RecordId'
),
#where = #where + CONCAT(
N' AND t',
ROW_NUMBER() OVER (ORDER BY (SELECT 1)),
N'.RecordId is NULL'
)
FROM STRING_SPLIT(#Tables, N',') s
SET #stm = CONCAT(
N'UPDATE t SET [Status] = 100 ',
N'FROM TemporaryTable t',
#join,
N' WHERE ',
STUFF(#where, 1, 5, N'')
)
PRINT #stm
EXEC sp_executesql #stm
Notes:
One note, that I think is important - consider passing tables names using table value type for parameter, not as comma-separated text.
It seems like this will suit your needs, though I don't fully understand what you're trying to do. Here we're constructing the final SQL in two pieces (#s and #where) and then concatenating into the final SQL at the end.
declare #Tables varchar(100) = N'Customer,Employee,Owner'
declare #tablenames table (tablename nvarchar(100))
insert #tablenames (tablename)
select value
from string_split(#Tables, ',');
declare #where varchar(max) = ''
declare #s varchar(max) = '
update t
set [Status] = 100
from TemporaryTable t'
select #s += '
left join ' + tablename + ' on ' + tablename + '.RecordId = t.RecordId'
, #where += case when #where = '' then '' else ' and ' end + tablename + '.RecordId is null
'
from #tablenames
print #s + char(13) + ' where ' + #where
exec( #s + char(13) + ' where ' + #where)
I am working on a project with SSRS and Rockwell Software's RSView32. Basically this software project logs manufacturing data to individual tables as such:
One table (Machine1_TagTable) has Tag names which describe the data as such: TagName, TagIndex. The name provides a human-understandable reference to the information contained in a second table. Example : Part Number, 1
The second table (Machine1_FloatTable) contains raw data with nothing more than a timestamp, TagIndex and value.
Example : 2013-12-10 15:44:11.322, 1, 12345(value)
I have a dynamic pivot which works for ONE table; however, I would like to use a variable parameter passed from SSRS to select both the TagTable and FloatTable.
This works with the Machine1_FloatTable as part of the dynamic statement, but not in the XML path building. I understand this is a scope issue, so I'm looking for creative ways to allow me to pass the table names from SSRS into this stored procedure.
Here's what I have now:
DECLARE #FLOATTABLE NVARCHAR(MAX), #TAGTABLE NVARCHAR(MAX), #startdate NVARCHAR(MAX),
#enddate NVARCHAR(MAX), #cols as NVARCHAR(MAX), #query as NVARCHAR(MAX)
SET #TAGTABLE ='dbo.Machine1_TagTable'
SET #FLOATTABLE = 'dbo.Machine1_FloatTable'
SELECT #cols = STUFF((SELECT DISTINCT ',' + QUOTENAME(CONVERT(VARCHAR,TagName),'"')
FROM #tagtable
FOR XML PATH('')),1,1,'')
Set #query = 'SELECT DISTINCT DateAndTime, Millitm, ' + #cols + ' FROM ( select
T.DateAndTime, T.Millitm, N.TagName, T.Val from ' + #FLOATTABLE + ' T LEFT JOIN ' +
#TAGTABLE + ' N ON T.TagIndex=N.TagIndex WHERE T.DateAndTime Between '''+ #startdate +
''' AND '''+ #enddate +''') x PIVOT (MAX(Val) for TagName IN (' + #cols + ')) p'
PRINT (#query)
Any help or suggestions would be greatly appreciated. Thanks!
Well, it's possible, if I understand your question. You have to build a dynamic SQL string, and execute that to populate #cols.
declare #string nvarchar(MAX)
set #String = 'SELECT STUFF((SELECT DISTINCT '','' + QUOTENAME(CONVERT(VARCHAR,ActualDate),'
+ '''"'') FROM ' + #TAGTABLE + ' FOR XML PATH ('''')),1,1,'''')'
EXECUTE sp_executeSQL #String, #Cols OUTPUT
print #cols
It's pretty hacky, but I think it should work.
my stored procedeur like this:
alter PROCEDURE [dbo].[ParkingDeatailsReportnewstack]
#startdate NVARCHAR(100),
#enddate NVARCHAR(100)AS
BEGIN
DECLARE #cols AS NVARCHAR(MAX) , #query AS NVARCHAR(MAX)
SELECT #cols = STUFF(( SELECT DISTINCT ',' + QUOTENAME(Vtype)
FROM dbo.VType_tbl FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
SET #query =
'SELECT LocName,Date, ' + #cols + '
from (
select l.LocName,v.Vtype, convert(date, dtime) as Date
from Transaction_tbl t
join VType_tbl v on t.vtid = v.vtid
join dbo.Location_tbl l on t.locid=l.Locid
where dtime between ''' + #startdate + ''' and ''' + #enddate + ''' order by l.LocName
) d
pivot (
count(Vtype) for Vtype in (' + #cols + ')
) p '
EXEC sys.sp_executesql #query
End
i want to get my locname in ascending order but while giving
order by l.LocName getting error :The ORDER BY clause is invalid in views, inline functions, derived tables, subqueries
We can fix the problem very easily by adding a TOP 100 PERCENT clause into the view definition.
Try using this way -
http://www.sqlpassion.at/archive/2015/05/25/the-ambiguity-of-the-order-by-in-sql-server/
Try migrating your order by clause into the outermost select statement — the only place where order by makes any sense.
Something like
select LocName ,
...
from ...
order by LocName
Or, even simpler, wrap the original, complex select in an outer select whose sole purpose is ordering, along these lines:
select *
from ( select
...
) t
order by t.x, t.y , ...