How to use the EXEC command - sql

I am trying to get a result from a database that will be selected after knowing the country of a user given his email.
I tried using EXEC() but now I get too many results from different users when I clearly indicate that I only want the result from certain user in the 'where' clause.
I first tried making the query with pure inner joins but it failed, it indicated that there was a syntax error, but if i ran it separately without the exec it worked.
After that I decided to use sub-queries, but as I mentioned above, it is returning all of the values, as if it wasn't considering the 'where'
What am I doing wrong?
Here is a sample of the query:
DECLARE #email nvarchar(150) = 'name.lastname#mx.company.com'
--Getting the country code of user
DECLARE #country_code nvarchar(3) = (SELECT country_code FROM general.countries WHERE id_country = (SELECT fk_country FROM databases_access.staff WHERE email = #email))
--Setting user database to search for job title & department
DECLARE #dbname NVARCHAR(25)
SET #dbname = 'dsv_global_' + #country_code
Declare #query nvarchar(500)
-- Query to be run to to get the user department and job title
SET #query =
'
USE '+#dbname+'
SELECT
id_staff,
email,
(SELECT complete_name_dept FROM dsv_global.departments WHERE id_department = fk_department),
(SELECT CONCAT(title,'' '',description) FROM dsv_global.job_titles WHERE id_job_title = (SELECT fk_title FROM dsv_global.staff_information WHERE fk_staff = id_staff)) ,
(SELECT COUNT(fk_staff) FROM dsv_global.staff_managers WHERE fk_manager = fk_staff)
FROM dsv_global.staff
WHERE email = '''+#email+''' AND status = ''ACTIVE''
'
----Storing department & title from user in temp table
--DECLARE #user_info TABLE (id_staff int, email nvarchar(200),complete_name_dept nvarchar(100), title nvarchar(200),num_of_errors int)
--INSERT INTO #user_info
EXEC(#query)
Edit:
I expect to receive:
But I receive:

It's worth to use Common Table Expressions when you deal with complex queries. You can put WITH to define a temporary named result set that available temporarily in the execution scope of a statement. And, by another hand, put GROUP BY for your COUNT function. Also, you need to put id_staff inside the WITH block, it looks like:
WITH cte_titles ( job_title)
AS (
SELECT CONCAT(title,'' '',description)
FROM dsv_global.job_titles
WHERE id_job_title IN
(SELECT fk_title
FROM dsv_global.staff_information
WHERE fk_staff = id_staff)
),
cte_staff (count_staff) AS
(
SELECT COUNT(fk_staff)
FROM dsv_global.staff_managers
WHERE fk_manager = fk_staff
GROUP BY fk_staff
)
SELECT
dsv.id_staff,
dsv.email,
job_title,
count_staff,
FROM dsv_global.staff dsv
cte_staff,
cte_titles
WHERE email = '''+#email+''' AND status = ''ACTIVE''

The problem is that i was not assigning enough space to store the query inside a string :
I had:
Declare #query nvarchar(500)
i changed it to :
Declare #query nvarchar(500)

Related

SQL Server: set a variable with more than one possible value

I need your help. I have a command to update rows, this works if in a variable #ID contain one value.
DECLARE #ID nvarchar(100)
SET #ID = (select top 1 id from [SERVICES])
DECLARE #UPDATE nvarchar(max)
SET #UPDATE ='UPDATE SERVICES
SET SERVICES.options = t1.options
FROM SERVICES t
JOIN (SELECT *
FROM OPENQUERY([ORI], ''SELECT ID, options
FROM log
WHERE ID = ''''' + #ID + ''''' '')) t1 ON t1.id = t.id'
EXEC (#UPDATE)
but I need to update more than 1 rows.
If I specify a condition like this:
SET #ID = (SELECT id FROM [ReportSM].[dbo].[SERVICES])
I get an error:
Subquery returned more than 1 value.
How to fix it?
It sounds like you really want to pass a table valued parameter into the open query, but that's not supported. You can remove that filter and let the join take care of the update accuracy but that will result in a potentially much more expensive remote query than necessary. That solution would just look like this:
UPDATE
t
SET
t.options = t1.options
FROM
Services t
JOIN (SELECT ID, options FROM OPENQUERY([ORI], 'SELECT ID, options FROM
log')) t1 ON t1.id = t.id
However, if you have control over the ORI linked server, you could set up a linked server there back to your ReportSM server. That would let you create a view on your ORI server that contains all of the IDs from your [ReportSM].[dbo].[SERVICES] table which is what you are trying to filter your log table on. That means you could perform the ID filtering on the ORI side and then run a simpler update on the ReportSM side. Something like this on the ORI side:
CREATE VIEW vReportServiceIDs
AS
SELECT
ID
FROM
[ReportSM].[dbo].[SERVICES]
CREATE VIEW vReportServiceLogs
AS
SELECT
reportService.ID,
oriLog.options
FROM
vReportServiceIDs reportService
JOIN [log] oriLog ON reportService.ID = [log].ID
And then on your ReportSM side:
UPDATE
t
SET
t.options = t1.options
FROM
SERVICES t
JOIN (
SELECT
ID, options
FROM
OPENQUERY([ORI], 'SELECT ID, options FROM vReportServiceLogs')
If you do not have that kind of access to the ORI server and the logs table has too much data for you to just query it all and exclude what you don't need during the join, you might want to consider creating a cache of the logs table that you update from a job on the ReportSM server and then just joining on that during your update.
Option 1
In your current setup, you could pass #ID as a CSV to OPENQUERY such that WHERE IN would work.
WHERE ID = ' + #ID + '
could then be replaced with
WHERE ID IN (' + #IDs + ')'
Look here to convert your ID column into a CSV: SQL Server convert select a column and convert it to a string
Be aware of the limit on the length of the IN clause. See this https://dba.stackexchange.com/questions/14161/what-is-the-maximum-number-of-parameters-i-can-pass-using-the-sql-in-clause-in-s
Option 2
Since concatenating data directly into a query has SQL injection concerns, you could also look at a more structured approach of using FOR XML to convert the IDs into an xml fragment and passing that into OPENQUERY and within that reading the ids out using OPENXML.
If both your servers are SQL Server 2016 or above, you could also use JSON as your format for transferring the ids instead of XML. You would use FOR JSON to create a JSON array containing the ids and use OPENJSON on the destination SQL server to convert the JSON back into a rowset that you can join.
declare #json varchar(max) = (select id from [ReportSM].[dbo].[SERVICES] FOR JSON PATH)
This will generate a string like this
[{"id":1},{"id":2},{"id":3},{"id":4}]
You can add this into a variable in the query you are preparing and read it using below
SELECT ID
FROM OPENJSON (#json, '$')
WITH (ID IN '$.id')
Putting it together your query would look like this:
declare #json varchar(max) = (select id from [ReportSM].[dbo].[SERVICES] FOR JSON PATH)
DECLARE #UPDATE nvarchar(max)
SET #UPDATE ='UPDATE SERVICES
SET SERVICES.options = t1.options
FROM SERVICES t
JOIN (SELECT *
FROM OPENQUERY([ORI], ''DECLARE #json nvarchar(max) = ''''' + #json + '''''
SELECT ID, options
FROM log
WHERE ID IN (SELECT ID FROM OPENJSON (#json, ''$'') WITH (ID IN ''$.id'')))) t1 ON t1.id = t.id'
EXEC (#UPDATE)

No column name was specified for column 1 of 'src' Merge statement SQL server

I am using the MERGE statement in SQL server to refresh the data but I am repeatedly getting this error int the ON clause of the MERGE statement.
The code is
DECLARE #instance varchar(50)
DECLARE #db varchar (50)
DECLARE #queryEntity nvarchar(max)
SET #instance = (select value from Parameter where name = 'SERVERALIAS')
SET #db = (select value from Parameter where name = 'SERVERDB')
SET #queryEntity = 'Select EntityId,EntityName,CreatedDate,ModifiedDate,Active,TENANTID,PriorityId From [' + #instance + '].[' + #db + '].metadata.Entity Where TENANTID = 1'
MERGE [metadata].[Entity] AS trgt
USING ( VALUES(#queryEntity) ) AS src
ON ( **trgt.EntityId = src.EntityId** )
WHEN matched
--AND trgt.ModifiedDate <= src.ModifiedDate
THEN
-- if the master has a row newer than the client
-- update the client
UPDATE SET trgt.EntityId = src.EntityId,
trgt.EntityName = src.EntityName,
trgt.Createddate = src.CreatedDate,
trgt.ModifiedDate = src.ModifiedDate,
trgt.Active = src.Active,
trgt.TENANTID = src.TENANTID,
trgt.PriorityId = src.PriorityId
WHEN NOT matched BY SOURCE
THEN
DELETE
WHEN NOT matched BY TARGET
THEN
INSERT ( EntityId, EntityName, CreatedDate, ModifiedDate, Active, TENANTID, PriorityId)
VALUES ( src.EntityId, src.EntityName, src.CreatedDate, src.ModifiedDate, src.Active, src.TENANTID, src.PriorityId);
You haven't specified a column name for src -- the message is pretty clear. Try this:
MERGE [metadata].[Entity] AS trgt
USING ( VALUES(#queryEntity) ) AS src(EntityId)
--------------------------------------^
I should point out that this is only the beginning. src also doesn't have a host of other columns, specified in the rest of the MERGE. In fact, it is merely a string. MERGE doesn't execute a string just because it looks like a query.
You have three options. The first is to dispense with the variable and put the query string in the MERGE. But that doesn't seem possible because you have variable identifier names.
The second is to use dynamic SQL with the MERGE.
My recommendation, though, is to use dynamic SQL to create a view or to populate a table with a canonical name. Then use that for the MERGE statement.

How to write an attribute name to the select query dynamically

I have a table including:
ID Name Period0Id Period1Id Period2Id
What I would like to receive data based on a user-defined parameter #check.
Lets assume:
declare #check int = 1;
In this case I need to get Period1Id value from the table. So I need to have something like that:
Select ID, Name, StatusId = Period + #check + Id -- #check is the parameter
From mytable
However, my query is not working. How can I fix this?
Your table looks like it is not in first normal form.
Instead of three columns for Period0Id to Period2Id you could have a column for PeriodIndex with values of (0,1,2) and a single column for PeriodId and then it would be just a WHERE PeriodIndex = #Check
You can't select a column using string interpolation with a variable as you are attempting. You can use dynamic SQL to create the SQL String dynamically. Or simply hardcode the options if they all have the same dataype.
Select ID,
Name,
StatusId = CASE #Check WHEN 0 THEN Period0Id
WHEN 1 THEN Period1Id
WHEN 2 THEN Period2Id
END
From mytable
Here is an alternative way that will create dynamic columns, which is essentially using your original query:
DECLARE #check VARCHAR = 1
DECLARE #sqlquery NVARCHAR(MAX)
SET #sqlquery = N'SELECT ID, Name, StatusId = Period'+#check+'Id
FROM mytable'
EXEC sp_executesql #sqlquery

SQL query help - declaration of variables within a function

I'm trying to write a SQL function but an having problems with declaring the variables I need for use in the WHERE clause.
Here's the code:
CREATE FUNCTION fn_getEmployeePolicies(#employeeid smallint)
RETURNS TABLE
AS
DECLARE #empLoc varchar
DECLARE #empBusA varchar
DECLARE #empType varchar
#empLoc = SELECT Location FROM fn_getEmployeeDetails(#employeeid)
#empBusA = SELECT BusinessArea FROM fn_getEmployeeDetails(#employeeid)
#empType = SELECT Type FROM fn_getEmployeeDetails(#employeeid)
RETURN select PolicyId, PolicyGroupBusinessArea.BusinessArea, policysignoff.PolicyGroupLocation.Location, policysignoff.PolicyGroupEmployeeType.EmployeeType
from policysignoff.PolicyGroupPolicy
LEFT JOIN policysignoff.PolicyGroupBusinessArea on policysignoff.PolicyGroupBusinessArea.PolicyGroupId=policysignoff.PolicyGroupPolicy.PolicyGroupId
LEFT JOIN policysignoff.PolicyGroupLocation on policysignoff.PolicyGroupLocation.PolicyGroupId=policysignoff.PolicyGroupPolicy.PolicyGroupId
LEFT JOIN policysignoff.PolicyGroupEmployeeType on policysignoff.PolicyGroupEmployeeType.PolicyGroupId=policysignoff.PolicyGroupPolicy.PolicyGroupId
where BusinessArea = #empBusA
AND EmployeeType = #empType
AND Location = #empLoc
GO
The logic I am trying to build in is:
'given an employeeId, return all "applicable" policies'
An "Applicable" policy is one where the Business Area, Location and EmployeeType match that of the user.
I am trying to use another function (fn_getEmployeeDetails) to return the BusArea, Loc & EmpType for the given user.
Then with the results of that (stored as variables) I can run my select statement to return the policies.
The problem i am having is trying to get the variables declared correctly within the function.
Any help or tips would be appreciated.
Thanks in advance!
Without knowing what your error actually is, I can only say that you're properly not after using varchar as datatype without specifying length.
DECLARE #empLoc varchar will declare a varchar with length 1.
Chances are it should be something like varchar(255) or similar.
Second to set variables you'll either need to use SET and use paranthisis for selects or set it into the statement:
SET #empLoc = (SELECT Location FROM fn_getEmployeeDetails(#employeeid))
or
SELECT #empLoc = Location FROM fn_getEmployeeDetails(#employeeid)
There are subtle differences between these two methods, but for your purpose right now I don't think it's important.
EDIT:
Based on your comment you lack a BEGIN after AS, and an END before GO.
Basically - your function syntax is mixing up "inline" table function with "multi-statement" function.
Such a function "template" should look something like this:
CREATE FUNCTION <Table_Function_Name, sysname, FunctionName>
(
-- Add the parameters for the function here
<#param1, sysname, #p1> <data_type_for_param1, , int>,
<#param2, sysname, #p2> <data_type_for_param2, , char>
)
RETURNS
<#Table_Variable_Name, sysname, #Table_Var> TABLE
(
-- Add the column definitions for the TABLE variable here
<Column_1, sysname, c1> <Data_Type_For_Column1, , int>,
<Column_2, sysname, c2> <Data_Type_For_Column2, , int>
)
AS
BEGIN
-- Fill the table variable with the rows for your result set
RETURN
END
GO
(script taken from sql server management studio)

SQL Error - Only one expression can be specified in the select list when the subquery is not introduced with EXISTS

I am trying to create a script/SP to extract data from a table and and send an email in HTML format with the data included in the email. I have the following written but get the above error but not entirely sure where to 'fix' the subquery. The query at the moment is:
DECLARE #tableHTML NVARCHAR(MAX) ;
SET #tableHTML =
N'<H1>Ad Groups and Users</H1>' +
N'<table border="1">' +
N'<tr><th>AD Group Name</th><th>Number of Users</th>' +
CAST (
(SELECT Group_Name,
COUNT(*) AS 'Number of Users'
FROM TABLE
WHERE Group_Name IN (
'PA_OptiTime',
'PA_P6',
'PA65_BIW',
'PA65_CAFM',
'PA65_CoinsOA',
'PA65_ConquestQ',
'PA65_Desktop_Osborne',
'PA65_MicrosoftAccess',
'PA65_MicrosoftOfficeStandard',
'PA65_MicrosoftOneNote',
'PA65_MicrosoftProject',
'PA65_MicrosoftVisio',
'PA65_MicrosoftVisioViewer',
'PA65_XStandard',
'SA65_AdobeCSSuite',
'SA65_AdobeProfessional',
'SA65_AdobeReader',
'SA65_AdobeStandard',
'SA65_AstaPowerproject',
'SA65_BentleyProjectwise',
'SA65_BravaReader',
'SA65_BravaViewer',
'SA65_CoinsPlus',
'SA65_GemSafe',
'SA65_GoogleEarth',
'SA65_NavisworksFreedomViewer',
'SA65_NRG',
'SA65_Offline',
'SA65_PDFEditor',
'SA65_PolycomCMADesktop',
'SA65_PrimaveraP6',
'SA65_SelectHR',
'SA65_ShareRegister',
'SA65_SketchUp',
'SA65_Xpress3DViewer'
)
GROUP BY Group_Name
)
AS NVARCHAR(MAX)
) +
N'</table>' ;
--create full body text
DECLARE #BodyText varchar(255)
DECLARE #SubjectText varchar(255)
SELECT #SubjectText = 'Test Email'
SELECT #BodyText = #tableHTML
--send message
EXEC EXSP_SendSMTPMailCDO
#To='email#work.com',
#Subject = #SubjectText,
#Body = #BodyText,
--#HTMLFormat = 0,
#body_format = 'HTML',
#From = 'email#work.com',
#Server = 'smtprelay'
Any help would be appreciated.
Thank you
You are attempting to cast a sub query which returns two columns to an NVARCHAR. Sql Server is not going to understand how to aggregate two columns into a single text field and is going to throw the error you received.
CAST ((SELECT Group_Name,
COUNT(*) AS 'Number of Users'
FROM TABLE
WHERE Group_Name IN (
'PA_OptiTime'
)
GROUP BY Group_Name
) AS NVARCHAR(MAX)
EDIT: Even reducing the sub query to return a single column won't work. The issue is it looks like you are attempting to append a line of XML for each group. So while the original error is produced by attempting to cast multiple columns in SQL, fixing that problem won't produce a query which appends a line of XML for each group.