Help me writing this query - sql-server

CREATE PROCEDURE [dbo].[sp_SelectRecipientsList4Test] --'6DBF9A01-C88F-414D-8DD9-696749258CEF','Emirates.Description','0','5'
--'6DBF9A01-C88F-414D-8DD9-696749258CEF',
--'121f8b91-a441-4fbf-8a4f-563f53fcc103'
(
#p_CreatedBy UNIQUEIDENTIFIER,
#p_SortExpression NVARCHAR(100),
#p_StartIndex INT,
#p_MaxRows INT
)
AS
SET NOCOUNT ON;
IF LEN(#p_SortExpression) = 0
SET #p_SortExpression = 'Users.Name Asc'
DECLARE #sql NVARCHAR(4000)
SET #sql='
DECLARE #p_CreatedBy UNIQUEIDENTIFIER
SELECT
Name,
POBox,
EmirateName,
TelephoneNo,
RecipientID,
CreatedBy,
CreatedDate,
ID
FROM
(
SELECT Users.Name, Users.POBox, Emirates.Description As EmirateName,
UserDetails.TelephoneNo, AddressBook.RecipientID,AddressBook.CreatedBy, AddressBook.CreatedDate,
AddressBook.ID,
ROW_NUMBER() OVER(ORDER BY '+ #p_SortExpression +') AS Indexing
FROM AddressBook INNER JOIN
Users ON AddressBook.RecipientID = Users.ID INNER JOIN
UserDetails ON Users.ID = UserDetails.UserID INNER JOIN
Emirates ON Users.EmiratesID = Emirates.ID
----WHERE (AddressBook.CreatedBy = #p_CreatedBy)
) AS NewDataTable
WHERE Indexing > '+ CONVERT(NVARCHAR(10), #p_StartIndex) +
' AND Indexing<=(' + CONVERT (NVARCHAR(10),#p_StartIndex ) + ' + '
+ CONVERT(NVARCHAR(10),#p_MaxRows)+') '
EXEC sp_executesql #sql
This query is not giving any error but also not giving any result
please help

Have you tried breaking down the statement, to check if intermediate results are as expected? That's what you do to debug a complex statement...
For example, there's a nested SELECT in there. If you commit that SELECT on its own, does it print the expected values?
Edit: There's a saying about teaching a man to fish. 'ck' and 'n8wrl' have given you fish to eat today, now please practice fishing to feed you tomorrow...

Well, a quick glance of this:
WHERE Indexing > '+ CONVERT(NVARCHAR(10), #p_StartIndex) + ' AND Indexing<=(' + CONVERT (NVARCHAR(10),#p_StartIndex ) +...
looks like you're looking for an impossible condition, not unlike this:
WHERE Indexing > 5 AND Indexing <= 5
So that might be why you're getting no rows, but this proc is ripe for SQL injection attacks too. Building SQL on the fly based on possibly-unvalidated parameters is very dangerous.

You are querying:
'WHERE Indexing > '+ CONVERT(NVARCHAR(10), #p_StartIndex) +
' AND Indexing<=(' + CONVERT (NVARCHAR(10),#p_StartIndex ) + ' + '
and then adding max rows as a string, you can do this much more easily like so:
'WHERE Indexing > '+ CONVERT(NVARCHAR(10), #p_StartIndex) +
' AND Indexing <='+ CONVERT(NVARCHAR(10),#p_StartIndex + #p_MaxRows)
EDIT
The problem with your inner WHERE is that you are passing in the parameter, you need to do
'WHERE (AddressBook.CreatedBy = ''' + CAST(#p_CreatedBy AS CHAR(36)) + ''')'

Are you sure all your joins should be inner joins?

Change sp_executesql to PRINT and see what gets generated (the poor man's debugger)
Besides what all the other people told you,
give me one good reason why you are using sp_executesql over exec? You are not using parameterized statements, you also are not protected from sql injections because you just execute the whole string
This will just bloat the procedure cache everytime this is run and some values change, you will get a new plan every time
Please take a look at Changing exec to sp_executesql doesn't provide any benefit if you are not using parameters correctly and Avoid Conversions In Execution Plans By Using sp_executesql Instead of Exec

Related

Dynamically PIVOT on ONE AND ONLY ONE column from a single TABLE

I've read multiple threads on this topic, and there does seem to be a way to do this dynamically, but I'm getting a syntax/compile error on the code.
I am trying to dynamically pull multiple rows for a temp table that simply has one column. The definition of the temp table is one field called acct_name. The challenge seems to be the "dynamics" of the fact that there will be over 50+ rows in the temp table, which is subject to change at any time.
I followed a previous poster's example, but am still getting compile/runtime errors.
I'm a rather novice SQL person, so you'll have to excuse the crudity of my code or my question.
DECLARE #idList varchar(500)
SELECT #idList = COALESCE(#idList + ',', '') + acct_name
FROM ##tmp_accts
DECLARE #sqlToRun varchar(1000)
SET #sqlToRun = '
SELECT *
FROM (
SELECT acct_name FROM ##tmp_accts
) AS src
PIVOT (
MAX(acct_name) FOR acct_name IN ('+ #idList +')
) AS pvt'
EXEC (#sqlToRun)
Does anyone have an obvious suggestion, I think it's very close to working.....
FOR EXAMPLE,
Let's say for sake of example we have the following acct_names - '12345','23456','34567','45678'.
The desire result is to return one row with 4 columns each with the respective value of acct_name. HOWEVER, the acct name is dynamic and is not known in advance, nor is the count of acct_name known in advance. A temp table is generated on the fly which determines all of the relevant acct_names for that particular run. It will vary with each run, each day that the query is run.
Thank you.....
Thru an article available thru Microsoft, the following solution does the job apporpriately.....
DECLARE
#columns NVARCHAR(MAX) = '',
#sql NVARCHAR(MAX) = '';
-- select the category names
SELECT
#columns+=QUOTENAME(acct_name) + ','
FROM
##tmp_accts
GROUP BY
acct_name;
-- remove the last comma
SET #columns = LEFT(#columns, LEN(#columns) - 1);
-- construct dynamic SQL
SET #sql ='
SELECT * FROM
(
SELECT DISTINCT acct_name
FROM
##tmp_accts
) t
PIVOT(
COUNT(acct_name)
FOR acct_name IN ('+ #columns + ')
) AS piv;';
-- execute the dynamic SQL
EXECUTE sp_executesql #sql;

Select unique values from every column in every table

Is there a way to get a count of distinct values from every table and column in SQL Server.
I've tried using a cursor for this, but that seems insufficient.
I've got to agree with Sean and say that this is going to be horrifically slow, but if you really want to do it, then I'm not going to stop you.
Something like this could be used as a starting point if you specifically don't want to use a cursor. This took just under a minute to look at a small database I've got with 10 tables in it. The largest table has just a few million rows in it. No matter what, you're going to be doing some sort of iteration, whether that's a cursor or explicitly reading against the table for each column.
Also, if you want to do something like this, you'll likely need to accommodate for things... like you're not going to be able to use COUNT on xml columns. Like I said, it's a starting point.
DECLARE #cmd VARCHAR(MAX)
SELECT #cmd =
STUFF (
(
SELECT
' union SELECT ''['+ SCHEMA_NAME(st.schema_id) + '].[' + st.name +']'' as [Object], ''[' + sc.name + ']'' as [Column], COUNT(distinct [' + sc.name + ']) as [Count] FROM [' + SCHEMA_NAME(st.schema_id) + '].[' + st.name + ']'
FROM sys.tables st
JOIN sys.columns sc
ON sc.object_id = st.object_id
JOIN sys.dm_db_partition_stats ddps
ON ddps.object_id = sc.object_id
WHERE
ddps.row_count > 0
FOR XML PATH('')
),1,6,''
)
EXECUTE (#cmd)

SQL Server - Select from a variable amount of tables depending on date range

I have a query that joins multiple Data Sources together, I need a query that will select from a variable amount of tables depending on the date range I send it.
Joining Query
SELECT I.SerialNumber as DataSource,Deployed,Removed
FROM InstrumentDeployments ID
INNER JOIN Instruments I On I.Id = ID.InstrumentId
INNER JOIN Points P On P.Id = ID.PointId
WHERE P.Id = 1
ORDER BY Deployed
Joining Query Result
So from the above query result, if I wanted to select all of the historical information, it would go through and get the data from the specific tables
(called DataSource in query above) dependant on the relevant date.
Final Query - Something like this but the variable tables from query result above.
SELECT * FROM (VariableTables) WHERE DateRange BETWEEN '2016-09-07' and '2018-07-28'
Thanks
Please note that this is completely untested as the sample data is an image (and I can't copy and paste text from an image). If this doesn't work, please provide your sample data as text.
Anyway, the only way you'll be able to achieve this is with Dynamic SQL. This also, like in the comments, assumes that every table has the exact same definition. if it doesn't you'll likely get a failure (perhaps a conversion error, or that for a UNION query all tables must have the same number of columns). If they don't, you'll need to explicitly define your columns.
Again, this is untested, however:
DECLARE #SQL nvarchar(MAX);
SET #SQL = STUFF((SELECT NCHAR(10) + N'UNION ALL' + NCHAR(10) +
N'SELECT *' + NCHAR(10) +
N'FROM ' + QUOTENAME(I.SerialNumber) + NCHAR(10) +
N'WHERE DateRange BETWEEN #dStart AND #dEnd'
FROM InstrumentDeployments ID
INNER JOIN Instruments I ON I.Id = ID.InstrumentId
INNER JOIN Points P ON P.Id = ID.PointId
WHERE P.Id = 1
ORDER BY Deployed
FOR XML PATH(N'')),1,11,N'') + N';';
PRINT #SQL; --Your best friend.
DECLARE #Start date, #End date;
SET #Start = '20160907';
SET #End = '20180728';
EXEC sp_executesql #SQL, N'#dStart date, #dEnd date', #dStart = #Start, #dEnd = #End;

Creating a Table Valued Function with dynamic columns

I received help previously on creating a query for listing total sales by month for a list of jobs. However, I cannot put this into a view as a variable needs declared.
I thought I could use a Table Valued Function (which I've never used before) but apparently I need to define the columns of the table which I cannot do if they are to be dynamic (the columns are yyyy-mm).
Can someone suggest how I could approach this problem please?
I am not familiar with creating anything other than views so this is completely new to me. Obviously if using the TVF isn't the best method please advise.
The query:
Declare #SQL varchar(max) = Stuff((Select Distinct ',' + QuoteName(YearMonth) From v_JobSalesByMonth Order by 1 For XML Path('')),1,1,'')
Select #SQL = '
Select [JobID], [TotalJobSales],' + #SQL + '
From (
Select JobID
,TotalJobSales = sum(SalesForMonth) over (Partition By JobID)
,YearMonth
,SalesForMonth
From v_JobSalesByMonth A) A
Pivot (sum(SalesForMonth) For [YearMonth] in (' + #SQL + ') ) p'
Exec(#SQL);
I started to create the function but am confused about how to define the dynamic columns.
CREATE FUNCTION db.tvfnJobSalesByMonthPivot (#JobID INT)
RETURNS #JobSalesByMonth TABLE
(
JobID int PRIMARY KEY NOT NULL,
TotalJobSales decimal(6,2) NULL,
2016-12 (ermmmmm?)
)
Many thanks in advance.
Paul

Generate column name dynamically in sql server

Please look at the below query..
select name as [Employee Name] from table name.
I want to generate [Employee Name] dynamically based on other column value.
Here is the sample table
s_dt dt01 dt02 dt03
2015-10-26
I want dt01 value to display as column name 26 and dt02 column value will be 26+1=27
I'm not sure if I understood you correctly. If I'am going into the wrong direction, please add comments to your question to make it more precise.
If you really want to create columns per sql you could try a variation of this script:
DECLARE #name NVARCHAR(MAX) = 'somename'
DECLARE #sql NVARCHAR(MAX) = 'ALTER TABLE aps.tbl_Fabrikkalender ADD '+#name+' nvarchar(10) NULL'
EXEC sys.sp_executesql #sql;
To retrieve the column name from another query insert the following between the above declares and fill the placeholders as needed:
SELECT #name = <some colum> FROM <some table> WHERE <some condition>
You would need to dynamically build the SQL as a string then execute it. Something like this...
DECLARE #s_dt INT
DECLARE #query NVARCHAR(MAX)
SET #s_dt = (SELECT DATEPART(dd, s_dt) FROM TableName WHERE 1 = 1)
SET #query = 'SELECT s_dt'
+ ', NULL as dt' + RIGHT('0' + CAST(#s_dt as VARCHAR), 2)
+ ', NULL as dt' + RIGHT('0' + CAST((#s_dt + 1) as VARCHAR), 2)
+ ', NULL as dt' + RIGHT('0' + CAST((#s_dt + 2) as VARCHAR), 2)
+ ', NULL as dt' + RIGHT('0' + CAST((#s_dt + 3) as VARCHAR), 2)
+ ' FROM TableName WHERE 1 = 1)
EXECUTE(#query)
You will need to replace WHERE 1 = 1 in two places above to select your data, also change TableName to the name of your table and it currently puts NULL as the dynamic column data, you probably want something else there.
To explain what it is doing:
SET #s_dt is selecting the date value from your table and returning only the day part as an INT.
SET #query is dynamically building your SELECT statement based on the day part (#s_dt).
Each line is taking #s_dt, adding 0, 1, 2, 3 etc, casting as VARCHAR, adding '0' to the left (so that it is at least 2 chars in length) then taking the right two chars (the '0' and RIGHT operation just ensure anything under 10 have a leading '0').
It is possible to do this using dynamic SQL, however I would also consider looking at the pivot operators to see if they can achieve what you are after a lot more efficiently.
https://technet.microsoft.com/en-us/library/ms177410(v=sql.105).aspx

Resources