SSRS error adding shared dataset - sql-server

When I add the following code into my report stored procedure, I cannot add the shared dataset to my SSRS report, and it gives me this error:
Incorrect syntax near the keyword 'as'.
The code I have added (to a previously working procedure) is:
set #sqlstring = Concat(N'INSERT INTO #TEXT_SEARCH_RESULT',
N'(Result_Name,Count_of_tickets,HT,Created_Group)',
N' SELECT ',#TimeDefinition,' as RESULT_NAME',
N' ,sum(COUNT_OF_TICKETS) as COUNT_OF_TICKETS',
N' ,sum(HT) as HT',
N' ,''Group'' as CREATED_GROUP',
N' from #TEXT_SEARCH_MAIN ct',
N' where (1=1) ')
--other items that append to the string, but have no bearing on the question
set #sqlstring=concat(#sqlstring,N' group by ',#TimeDefinition)
The sql statement appends data to an temporary table with data already in it from the previous statement.
#TimeDefinition is declared as NVARCHAR(100)
Replacing #TimeDefinition with the text that is in the variable (example cast(CREATEDDATE as date) allows the report to be added
if I remove the AS from the concatenated string, then the error simply changes to Incorrect syntax near the keyword 'by'.
Running the query in SSMS does not give an error

The problem here is that SSRS wasn't able to determine the columns of the query without a parameter being supplied. When using dynamic SQL, sometimes the dynamic SQL won't have a value if no value is supplied for one (or more) parameters; thus the column values can't be determined. Take a simple example like:
DECLARE #SQL nvarchar(MAX);
SET #SQL = N'SELECT ' + QUOTENAME(#Col1) + N' AS Result1,' + NCHAR(10) +
N' ' + QUOTENAME(#Col2) + N' AS Result2' + NCHAR(10) +
N'FROM MyTable;';
PRINT #SQL;
EXEC sp_executesql #SQL;
Unless values for #Col1 and #Col2 are supplied, then the value of #SQL will result in NULL.
When using queries such as these, SSRS will therefore present you with a dialogue window. This'll look something like the below (taken from SSDT 2017)
You'll need to enter some values into this dialogue (in the second column, which is named Parameter Value) so that SSRS can correctly determine the column definitions. Once you've done that, SSRS will create the dataset correctly.
On a related note, I suggest against the use of 1=1. This just adds time to the processing (as the data engine still needs to check if 1=1) and can cause the query analyser to make poor choices. It actually can be considered quite bad practice to put things like that in your query's WHERE clause.

Related

Adding a new column to a table getting the column name from another table

I'm using Microsoft SQL server management studio.
I would like to add a new column to a table (altertable1), and name that column using the data from a cell (Date) of another table (stattable1).
DECLARE #Data nvarchar(20)
SELECT #Data = Date
FROM stattable1
WHERE Adat=1
DECLARE #sql nvarchar(1000)
SET #sql = 'ALTER TABLE altertable1 ADD ' + #Data + ' nvarchar(20)'
EXEC (#sql)
Executing this, I get the following error and can't find out why:
"Incorrect syntax near '2021'."
The stattable1 looks like this:
Date |Adat
2021-09-08 |1
2021-09-08 is a daily generated data:
**CONVERT(date,GETDATE())**
Just like Larnu said in comment, maybe this is not a main problem for you, but if you want to do this add [ ] when you want to name column starting with number.
Like this:
SET #sql = 'ALTER TABLE altertable1 ADD [' + #Data + '] nvarchar(20)'
And of course, naming columns by date or year is not best practice.
The problem with your overall design is that you seem to be adding a column to the table every day. A table is not a spreadsheet and you should be storing data for each day in a row, not in a separate column. If your reports need to look that way, there are many ways to pivot the data so that you can handle that at presentation time without creating impossible-to-maintain technical debt in your database.
The problem with your current code is that 2021-06-08 is not a valid column name, both because it starts with a number, and because it contains dashes. Even if you use a more language-friendly form like YYYYMMDD (see this article to see what I mean), it still starts with a number.
The best solution to the local problem is to not name columns that way. If you must, the proper way to escape it is to use QUOTENAME() (and not just manually slap [ and ] on either side):
DECLARE #Data nvarchar(20), #sql nvarchar(max);
SELECT #Data = Date
FROM dbo.stattable1
WHERE Adat = 1;
SET #sql = N'ALTER TABLE altertable1
ADD ' + QUOTENAME(#Data) + N' nvarchar(20);';
PRINT #sql;
--EXEC sys.sp_executesql #sql;
This also demonstrates your ability to debug a statement instead of trying to decipher the error message that came from a string you can't inspect.
Some other points to consider:
if you're declaring a string as nvarchar, and especially when dealing with SQL Server metadata, always use the N prefix on any literals you define.
always reference user tables with two-part names.
always end statements with statement terminators.
generally prefer sys.sp_executesql over EXEC().
some advice on dynamic SQL:
Protecting Yourself from SQL Injection - Part 1
Protecting Yourself from SQL Injection - Part 2

Need help/explanation on how to properly add parameter to dynamic SQL Query

I'm searching through databases and I reached out to StackOverflow for some help in how to learn how to run the dynamic SQL through.
The answer I got was extremely useful but it didn't come with an explanation of what was happening exactly or why. Now I'm trying to add another parameter to the code and I'm having trouble adding it.
I would like someone to help me correct the parameter so its input correctly and explain what is going on in the dynamic SQL statement.
Lets get to the problem with the Query. Everything works great as it was correctly by another StackOverflow poster. But now I need to add where the 'ControlID' column must start with the letter Q.
With the way I've put it in now I'm getting an error that states the column 'ControlID' does not exist. But when I check to see if ControlID is the correct column name
select * FROM [EDDS1111111].[EDDSDBO].[Document] where ControlID like 'Q%'
I do get results. So it's not an invalid column name.
I designed this input of 'ControlID' to be similar to the way artifact ID was added earlier in the code so I'm confused as to why I'm getting this error.
-- this is used to add line breaks to make code easier to read
DECLARE #NewLine AS NVARCHAR(MAX) = CHAR(10)
-- to hold your dynamic SQL for all rows/inserts at once
DECLARE #sql NVARCHAR(MAX) = N'';
-- create temp table to insert your dynamic SQL results into
IF OBJECT_ID('tempdb..#DatabaseSizes', 'U') IS NOT NULL
DROP TABLE #DatabaseSizes;
create table #DatabaseSizes(
controlid nvarchar(128),
fileSize DECIMAL (10,6),
extractedTextSize DECIMAL(10,6)
)
SELECT #sql = #sql + N'' +
'select SUM(fileSize)/1024/1024/1024 as fileSize,
SUM(extractedTextSize)/1024/1024 as extractedTextSize ' + #NewLine +
'FROM [EDDS' + CAST(ArtifactID as nvarchar(128)) + '].[EDDSDBO].
[Document] ed' + #NewLine +
'where ed.CreatedDate >= (select CONVERT(varchar,dateadd(d,-
(day(getdate())),getdate()),106)) and ed.controlid = '+Cast(Controlid as
nvarchar(128))+'%' + #NewLine + #NewLine
FROM edds.eddsdbo.[Case]
WHERE name like '%Review%' and (StatusCodeArtifactID = '1780779' or
StatusCodeArtifactID = '1034288')
--controlid always needs to begin with a Q
-- for testing/validating
PRINT #sql
INSERT INTO #DatabaseSizes (
controlid,
fileSize,
extractedTextSize
)
-- executes all the dynamic SQL we just generated
EXEC sys.sp_executesql #SQL;
As I've put the code so far I'd expect for the controlID to equal the current control ID where it would begin with a Q.
I tried to copy the part where it was done correctly so I'm a little confused. Any help to increase my understanding is greatly appreciated.
Thank you for your time
There is not a column for ControlID within edds.eddsdbo.[case]. But it does exist within all databases located within FROM [EDDS' + CAST(ArtifactID as nvarchar(128)) + '].[EDDSDBO].[Document]. CreatedDate also does not exist within the .[Case] tables but does exist within the .Document databases. That is why I put the search for ControlID next to the where statement for the .Document section of the query
This is what was returned when I try to run the code.
Invalid column name 'Controlid'.

SQL injection in SP_EXECUTESQL

I have a stored procedure in SQL Server which gets a XML as input parameter. In this XML is defined - what stored procedure with which parameters values should be executed. And according to that the stored procedure execute the wanted one using dynamic SQL with sp_executesql.
The problem is that the values of the parameters are vulnerable to SQL injection.
I have tried using typed parameters like that:
EXEC sys.sp_executesql
#stmt = #sql,
#params = N'#Username SYSNAME, #HireDate DATE',
#UserName = #Username, #HireDate = #HireDate;
But it doesn't really work in my case, because I don't know what procedure with what parameters will be executed. The number of parameters may vary and some of them are optional / have default values etc. All I can get is the names of the parameters as string :(
After some parsing of the input XML the SQL Query is build and executed like that
declare #params nvarchar(max);
select #params = coalesce(#params + N', ', N' ') + r.attrName + N' = ' + iif(p.isNumericType = 1, r.Value, '''' + r.Value /*cast(r.Value as nvarchar(max))*/ + '''') --+ r.Value
from dbo.#ruleConfig r
left join #spParams p on p.paramName = r.attrName -- datatype of a parameter from information_schema.parameters
declare #sql nvarchar(max) = (select #procName + isnull(#params, N''));
exec dbo.sp_executesql #sql
The value of #sql can look something like that:
'core.GetUser #LogonName = 'myDomain\myLogon''
But also can look like that:
'core.GetUser #fullLogonName = 'myDomain\myLogon;'WAITFOR DELAY '0:0:20';--'' and that's the problem.
To begin with, if you have any functionality that requires you to send dynamic SQL stored procedures execution commands, you have a big problem with your design as with a strict tightly-coupled design, each API call will be mapped to a single stored procedure.
If you still wish to stick with the current design, than you have to create a list of known stored procedures and their values. You cannot just accept the fact that you "don't know what procedure with what parameters will be executed" because than your publish an SQL Injection functionality by-design.
You need to create an enum of the known stored procedures (for example 1 = proc1, 2 = proc2 etc.) and perform a regular-expression-based input validation to their parameters.
In the example you just gave, if you want "myDomain\myLogon" to be accepted and "myDomain\myLogon;'WAITFOR DELAY '0:0:20';--" to be rejected, you can for example use a regular expression that looks like this:
^([A-Za-z\\])*$
You can test it in the regexr website.
You'll have to create an input validation regex for each field type - for example names, usernames, emails etc. Basically it's pretty similar to just creating a separate API for each procedure call, with proper input validation.

How to compare a single stored procedure across 100s of databases? T-SQL

I need a way to compare a stored procedure across multiple (hundreds and hundreds) of databases. Not just compare two at a time. It looks like ApexSQL only does two at a time (unless I am mistaken). This won't work because it will take forever. I need to group by the stored procedure so I can identify which groups I can make certain changes to.
I tried the following code solution:
select
'select OBJECT_NAME(object_id), OBJECT_DEFINITION(object_id) from ' +
name + '.sys.procedures where name like ''sp_someProcedure%'' union'
from
master.sys.databases
I then tried to throw the scripts that outputted into a sub select where I do a group by the stored procedure. This doesn't work because for some reason you can't union between multiple databases for sys stuff (unless I am mistaken). Each select statement stays in the context of what you are using. So use databaseOne stays in databaseOne even though the following select statement is select blah blah blah from databaseTwo.
Any thoughts?
Firstly, let's start off topic with a useful article: Is the sp_ prefix still a no-no?. Don't start your SP's name with sp_, it's reserved by Microsoft for system stored procedures. The article discusses further as to why it's a bad idea; but simply put, your SP could just (suddenly) stop working one day, and also it can have a performance hit.
Now, more on topic. You could instead use the undocumented sp sp_msforeachdb. This results in something like:
CREATE TABLE #Procs (ObjectName sysname, ObjectDefination nvarchar(MAX));
DECLARE #SQL nvarchar(MAX);
SET #SQL = N'SELECT OBJECT_NAME(object_id), OBJECT_DEFINITION(object_id)' + NCHAR(10) +
N'FROM [?].sys.procedures' + NCHAR(10) +
N'WHERE [name] LIKE ''sp_someProcedure%'';';
INSERT INTO #Procs
EXEC sp_msforeachdb #SQL;
SELECT *
FROM #Procs;
DROP TABLE #Procs;

How can I join tables specified in a separate list of tables in SQL Server? [duplicate]

I have been researching this for a couple of days and feel like I am going around in circles. I have basic knowledge of SQL but there are many areas I do not understand.
I have a table that stores the names and fields of all the other tables in my database.
tblFields
===================================================
TableName FieldName BookmarkName
---------------------------------------------------
Customer FirstName CustomerFirstName
Customer LastName CustomerLastName
Customer DOB CustomerDOB
I want to write a SELECT statement like the following but i am unable to get it work:
SELECT (SELECT [FieldName] FROM [TableName]) FROM tblFields
Is this possible? The application I have developed requires this for user customization of reports.
If i understand what you are trying to do, i think this will help you. It is not pretty and it works for SQL Server 2005 and above, but maybe this is what you are looking for:
declare #tableName nvarchar(100)
declare #sqlQuery nvarchar(max)
declare #fields varchar(500)
set #tableName = 'YourTableName'
set #fields = ''
select #fields = #fields + QUOTENAME(t.fieldname) + ',' from (
select distinct fieldname from tblfields where tablename = #tableName)t
set #sqlQuery = 'select ' + left(#fields, LEN(#fields)-1) + ' from ' + QUOTENAME(#tableName)
execute sp_executesql #sqlQuery
Edit: As Martin suggested, i edited so that the columns and tablename are using QUOTENAME
If I understand correctly what you are trying to do, you are probably better off doing this as two separate queries from your program. One which gets the fields you want to select which you then use in your program to build up the second query which actually gets the data.
If it must be done entirely in SQL, then you will need to tell us what database you are using. If it is SQL Server, you might be able to user a cursor over the first query to build up the second query which you then execute with the sp_executesql stored procedure. But doing doing it outside of SQL would be recommended.

Resources