I am getting the following error while trying to create a trigger on a table which sends an email to me:
Only one expression can be specified in the select list when the subquery is not introduced with EXISTS
Basically what I have done is:
set #stu = (select name, year, gender from studentinformation where id = #id)
set #bod = ' Details changed for ' + #stu
Exec msdb.dbo.sp_send_dbmail #body = #bod;
I have just shown that part of code which I think would be having issue. I feel putting brackets is making SQL feel it's a subquery? Is that the reason? But then how do I send results of a query through a trigger via email? The select statement refers to another table not the one on which the trigger is based
you should specify your query in the #query parameter of sp_send_dbmail
exec sp_send_dbmail
#query = 'select name,year,gender from studentinformation where id = 1234',
... -- some other parameters
please refer to the documentation on sp_send_dbmail for further details
https://learn.microsoft.com/en-us/sql/relational-databases/system-stored-procedures/sp-send-dbmail-transact-sql?view=sql-server-2017
You can only have 1 value in #stu. You're trying to populate it with name, year and gender.
I suggest DECLARE for your other values, so somethign like
DECLARE #stu varchar(100)
DECLARE #MyYear INT
DECLARE #MyGender varchar(10)
set #stu = (select name from studentinformation where id=#id)
set #MyYear = (select Year from studentinformation where id = #id)
set #MyGender = (select gender from studentinformation where id = #id)
Related
In my SQL database, I would like to make a general purpose soft link table. Perhaps something similar the following:
create table soft_link(
id uniqueidentifier,
name varchar(255),
LINK uniqueidentifier,
TABLE varchar(255),
primary key(id));
Say I have object "b_object" in some other table in the database. The LINK column would be the the unique identifier of b_object, and TABLE would be the table in which b_object is stored within the database.
Now I want to make a single stored procedure "sp_ResolveSoftLink". This method will take an id of a soft link, look up the LINK and TABLE columns of the soft link, and then use the TABLE and LINK to query for b_object.
The following is NOT proper SQL syntax, but hopefully it helps illustrate my question. I want to combine these two queries into a single stored procedure and return the result of the second query:
select LINK, TABLE from soft_link where id = xxxxxx
select * from TABLE where id = LINK
-- return the result of the second select query
FUNDAMENTAL QUESTION: How/can I use the varchar return from one query to form another query in the same stored procedure in SQL?
You can create a Stored procedure like this.
Here, First you have to store value of LINK and Table in variable and then use these variable to form another Dynamic Query
CREATE PROC spName
#id INT
AS
BEGIN
Declare #LINKValue uniqueidentifier, #TABLE varchar(255),#SQL varchar(max)
SELECT #LINKValue = LINK , #TABLE = TABLE
FROM soft_link
WHERE soft_link id = #id
SET #SQL='SELECT * FROM';
SET #SQL = #SQL +' ' + #TABLE
SET #SQL = #SQL +' ' + 'WHERE ID =' +' ' + #LINKValue
PRINT #SQL -- SEE Here This is what you actually Want
--EXEC (#SQL) --Then Execute this
END
I am trying to create a procedure where the conditional statement changes based on the value that the user inputs. A simplified example of what I'm trying to achieve is below.
Create Procedure [LossRatioReport] (#construction AS VARCHAR(2), #state AS VARCHAR(2)) AS
DECLARE #construction_condition AS NVARCHAR(MAX)
DECLARE #construction AS VARCHAR(2)
SET #construction = '01'
SET #construction_condition = CASE #construction WHEN '01'
THEN #construction_condition = '('01', 'Frame Construction', 'Frame')'
ELSE #construction_condition = '00'
END
BEGIN
SELECT year, SUM(loss)
FROM Database_1
WHERE id IN (SELECT DISTINCT id FROM Database_2 WHERE construction = #construction_condition)
END
GO
I want to do it this way because there is a list of both integer and strings for each type and I don't want to rewrite the code over and over for each condition. But when I try and run my code, I keep getting incorrect syntax messages for the variables in the WHERE statement.
You'll need to use dynamic sql for this:
DECLARE #sql NVARCHAR(MAX)
SELECT #sql = 'SELECT year, SUM(loss)
FROM Database_1
WHERE id IN (SELECT DISTINCT id FROM Database_2 WHERE construction IN( ' + #construction_condition
EXEC sp_executesql #sql
As Sean Lange stated, something should probably be mentioned on SQL Injection. Be sure you are aware of the danger before implementing a solution with dynamic SQL. Here is an overview, and Google can tell you much, much more.
In my script i have few select statements and update statements, as an example
SELECT * from TABLE1
WHERE userID= 'US001'
UPDATE TABLE2
SET value= 'months'
WHERE userID='US001'
statements going so on, so in this i have to copy and paste userID to every statement.
i want to declare a variable and assign to userID to refer it, so i don't need to add userID number to every query and i need to execute
i have tried this
Delcare #theID userID
SET userID ='us001'
but didn't work it out
please let me know..
thanks
You'll need to declare the type, and assign it. In Sql Server, variables are prefixed with #, like so:
DECLARE #theID NVARCHAR(20);
SET #theID ='us001';
UPDATE TABLE2 SET value= 'months' WHERE userID=#theID;
DECLARE #theID varchar(10);
SET #theID = 'us001';
In your statement you are declaring your variable as userID, which is not a valid data type.
In addition to the previous answers, in SQL Server 2008 and higher you can also declare and set the variable in a single line.
DECLARE #UserID NVARCHAR(20) = 'us001';
This is what works for me under SQL2005 in a stored procedure:
DECLARE #name varchar(100)
SELECT #name = 'Robin'
// and this should be do the update
SET userID = #name
// or in you query it should be
WHERE userID = #name
What's a good way to dynamically create tables, then join together all the tables that I have created?
Background
I'm trying to write a stored procedure that collects data from various parts of the database and presents it for use. For example if I wanted to send invoices to both Alice and Bob, this stored procedure would find all the information for the invoice. The trick is that Alice and Bob don't always need all the information I have. If Bob is tax exempt but trades in simoleons, I'll need to include information about currency exchange rates but leave out tax info. I want to give the stored procedure an order number and get back results formatted to the customer.
My solution so far is to create a table holding customer requirements. Then my stored procedure checks the table: if currency data is required I create and populate #Currency, or #Taxes holds tax data and so on. I check the requirement, then create the table as needed. At the end I need to Join the data into one record set.
My problem is that I have a error saying
Incorrect syntax near the keyword 'table'
AND an error saying
Must declare the table variable #Summary
on line 1 where i define #Summary These errors only show up when I use dynamic SQL to join the tables. When I comment out the dynamic SQL and copy-paste the statements into the one it should create, I have the result I want.
Code
I tried to use the following dynamic SQL in Microsoft SQL Server Management Studio
create procedure <procedure> (#OrderNumber varchar(20)) AS
DECLARE #CustomerName varchar(35) -- retrieved based on #OrderNumber
DECLARE #SQLQuery nvarchar(500)
DECLARE #ParamDef nvarchar(500)
DECLARE #SummaryTable table
(
ID varchar(20) --=#OrderNumber
, <Stuff>
)
SET #SQLQuery = 'Select * From #SummaryTable'
SET #ParamDef = '#SummaryTable table'
IF EXISTS(Select <TableName> from CustRequirements where <TableName> = 1 AND Customer = #CustomerName)
BEGIN
--Create table
DECLARE #<TableName> table
(
ID varchar(20)
, <Stuff>
)
--Populate
Insert into <TableName> Select <RelevantData> From <DataSource> where <Condition based on #OrderNumber or #CustomerName>
--Pepare Dynamic SQL
Set #SQLQuery = #SQLQuery + ' FULL OUTER JOIN #<TableName> ON <TableName>.ID = SummaryTable.ID'
SET #ParamDef = ', #<TableName> table'
END
<repeat for other tables>
EXECUTE sp_executesql #SQLQuery, #ParamDef, #Summary, #LineItems, #Taxes, #Currency
Question
Is there something wrong with my code? Is there a better way to do this? The other option I though of was to have IF Exists trees with the entire JOIN statement at the bottom of each branch (Since I can't seem to interrupt the JOIN clause with IF's). The problem with that is that I'll need 2^n JOIN statements to join n possible tables. n in this case might be as high has 20 or 30.
I think I see a problem in the code (maybe 3 problems -- see "questionable" 1 & 2 below) --
1) [Changed on 10/21, after OP's comments] The big problem: table parameters passed in the final "EXECUTE sp_executesql #SQLQuery..." are sometimes not declared.
1a) #Summary is actually never declared... you declared and set #SummaryTable, then use #Summary.
Just change that to #SummaryTable (I did that in (4) below), and I think that will prevent your second error message ("Must declare the table variable #Summary").
1b) All the other tables are sometimes declared: each of their DECLARE statements are within an "IF EXISTS". I suggest either (I) make the declares unconditional (outide IF EXISTS), but still conditionally INSERT... or (II) make the format of the EXECUTE command vary with what's available.
(I doubt the unused table variables need anything in them....)
In point 4 below (added 10/21), I give an example that I have NOT TESTED with my own databases (that test would take a lot more time)... so please let me know how it goes...
2) [questionable 1] Simple case of mixed case ;) Note that the line...
Set #SQLQuery = #SqlQuery + ' FULL OUTER JOIN #<TableName> ON <TableName>.ID = SummaryTable.ID'
... first has an uppercase "SQL", then mixed-case "Sql".
Why I said this is "questionable" -- if your server's COLLATION is case-insensitive, what you typed above would be fine.
3) [questionable 2] You have both '#TableName' and '#Table Name' (with a space). I realize that may just be how you typed it, in posting your question.
Point (4), added in update to answer -- possible code
create procedure <procedure> (#OrderNumber varchar(20)) AS
DECLARE #CustomerName varchar(35) -- retrieved based on #OrderNumber
DECLARE #SQLQuery nvarchar(500)
DECLARE #ParamDef nvarchar(500)
DECLARE #SummaryTable table
(
ID varchar(20) --=#OrderNumber
, <Stuff>
)
SET #SQLQuery = 'Select * From #SummaryTable'
SET #ParamDef = '#SummaryTable table'
--Create table variables, though they may not be populated
DECLARE #LineItems
(
ID varchar(20)
, <Stuff>
)
DECLARE #Taxes
(
ID varchar(20)
, <Stuff>
)
DECLARE #Currencytable
(
ID varchar(20)
, <Stuff>
)
IF EXISTS(Select <TableName> from CustRequirements where <TableName> = 1 AND Customer = #CustomerName)
BEGIN
--Populate
Insert into <TableName> Select <RelevantData> From <DataSource> where <Condition based on #OrderNumber or #CustomerName>
--Prepare Dynamic SQL
Set #SQLQuery = #SQLQuery + ' FULL OUTER JOIN #<TableName> ON <TableName>.ID = SummaryTable.ID'
SET #ParamDef = ', #<TableName> table'
END
<repeat for other tables>
EXECUTE sp_executesql #SQLQuery, #ParamDef, #SummaryTable, #LineItems, #Taxes, #Currency
The solution I've decided tp use is to create a table then use ALTER TABLE to add columns as needed.
CREATE Table #Output
(
InvoiceNumber varchar(20)
, <stuff>
)
IF EXISTS(Select <ThisSegment> FROM CustRequirements WHERE <ThisSegment> = 1 AND Customer = #CustomerName)
BEGIN
ALTER TABLE #Output ADD <ThisSegment> <dataType>
UPDATE #Output
SET <ThisSegment> = <data> from <DataSource>
WHERE <InvoiceNumber = DataSource.invoice> AND <Other conditions>
END
<Repeat as needed>
I am trying to send a resultset via email using xp_sendmail.
I need to send the email when an earlier executed query has any results.
Got the query results into a table variable/temp table and then
in xp_sendmail, using
Declare #table_var table(...)
..query execution..
EXEC master.dbo.xp_sendmail #recipients = 'xx#xx.com',
#query = 'select * from #table_var'
it gives error saying that
#table_var must be declared.
Even if I use temporary table, the message I get is
cannot reference object in tempdb database.
Any ideas on this?
Thanks in advance
You'll need to use a real table for this. Try..
If exists (select * from sys.tables where name = 'mytable')
drop table mytable
Create Table mytable table(...)
..query execution..
EXEC master.dbo.xp_sendmail #recipients = 'xx#xx.com',
#query = 'select * from mydatabase.dbo.mytable'