SQL Server 2017 - Database mail stored procedure - sql-server-2017

I have a stored procedure which basically I want to do the following:
Create temp table (if not exists) and populate with data
Output the query to SSMS, and assigning the query a variable (#sql)
Using the query, e-mail the contents of the query to the recipients
My script is this:
Create Procedure ListDaysofYear(#year as integer)
as
Declare #sql as varchar(200), #DBqry as varchar(200),
#tab as char(1) = char(9)
Declare #dayofyear as bigint = 1
Declare #monthofyear as int = 1
Declare #day as int = 1
Declare #curDate as datetime
Declare #DB as varchar(40)
Declare #sql2 as varchar(40)
Set #curDate = datefromparts(#year, #monthofyear, #day)
Set #DB = 'msdb'
IF OBJECT_ID('tempdb.dbo.##daysofYear','U') IS NOT NULL
DROP TABLE ##daysofYear
--Print 'YES'
ELSE
CREATE TABLE ##daysofYear
(
cDate DATETIME PRIMARY KEY NOT NULL,
cMonth VARCHAR(20) NOT NULL,
cDay VARCHAR(20) NOT NULL
)
WHILE year(#curDate) = #year
BEGIN
-- Insert rows based on each day of the year
INSERT INTO ##daysofYear (cDate, cMonth, cDay)
VALUES( (#curDate),
(DATENAME([MONTH], #curDate)),
(DATENAME([WEEKDAY], #curDate)) )
SET #curDate = #curDate + 1
END
--Output file to SSMS query window
Select dy.* from ##daysofYear dy;
Set #sql = 'Select dy.* from ##daysofYear dy;'
Set #sql2 = 'Use ' + #DB + '; Exec msdb.dbo.sp_send_dbmail
#profile_name = ''Notifications'',
#recipients = ''mikemirabelli6#hotmail.com'',
#attach_query_result_as_file = 1,
#query_attachment_filename = ''daysofyear.txt'',
#query_result_separator = '',
#body = ''The attached output file - DaysofYear table'',
#query = ''Select dy.* from ##daysofYear dy'' ;'
--Execute sp_sqlexec #sql
Exec(#sql2)
Basically when I run the execute line:
Exec dbo.ListDaysofYear 2018 ;
I get the following message the first time:
Msg 208, Level 16, State 0, Procedure dbo.ListDaysofYear, Line 25
[Batch Start Line 52] Invalid object name '##daysofYear
I believe it’s related to the "DROP TABLE" part of the T-SQL.
Thanks

Think i found the issue:
IF OBJECT_ID('tempdb.dbo.##daysofYear','U') IS NOT NULL <-- here you are dropping the table if exisit but doesn't create it so it throws an error in line 25 where it tries to insert data (to a table you dropped). i suggest replacing drop table with TRUNCATE TABLE.

Related

How do I pass input parameters to sp_executesql?

In SQL Server 2014, I am trying to create a dynamic WHERE clause.
I have built the query as a string, but when I try to execute it with sp_executesql, I get the following error:
Líne 13 You must declare the scalar variable "#desde".
I can't figure out how to get sp_executesql to recognize the input parameters.
ALTER PROCEDURE [dbo].[seleccionarFacturas]
-- Add the parameters for the stored procedure here
#desde char(8) = null,
#hasta char(8) = null,
#minimo int = null,
#ciudad int = null
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
DECLARE #tiendas varchar(max);
DECLARE #tablaFacturas TABLE
(
fecha char(8),
CO char(8),
consecutivo varchar(max),
nombreCliente varchar(max),
ventaTotal int
);
SET #tiendas='(ID_CO=20 OR ID_CO=22 OR ID_CO=23 OR ID_CO=27 OR ID_CO=35 OR ID_CO=39 OR ID_CO=45 OR ID_CO=48 OR ID_CO=55 OR ID_CO=58)';
DECLARE #dynamicCode nvarchar(max)=
N'
SELECT
FECHA_DCTO,
ID_CO,
DETALLE_DOC,
NOM_CLI_CONTADO,
(SUM(TOT_VENTA)) AS ventaTotal
FROM
moda.dbo.CMMOVIMIENTO_VENTAS
WHERE'
+ #tiendas +
N' AND FECHA_DCTO >= #desde
AND FECHA_DCTO <= #hasta
GROUP BY
DETALLE_DOC, ID_CO, FECHA_DCTO, NOM_CLI_CONTADO';
INSERT INTO #tablaFacturas
EXEC [dbo].[sp_executesql] #dynamicCode;
SELECT * FROM #tablaFacturas
Instead of
EXEC [dbo].[sp_executesql] #dynamicCode;
Use
EXECUTE sp_executesql #dynamicCode,
N'#desde char(8), #hasta char(8)',
#desde = #desde, #hasta = #hasta;
You have to define the parameters you used in the dynamic query like#desde and #hasta
Please refer sp_executesql
Else You can concat the values of #desde, #hasta to the dynamic query,
like
'....FECHA_DCTO >= ' + #desde +
'AND FECHA_DCTO <= ' + #hasta +
'GROUP BY ....'

Getting an error when passing the table name as parameter to a function

I am new to SQL query and here I am trying to get the complete name from dbo.Customer_List table and have written this code. However when try to run am getting the following error. I don't know what I am doing wrong.
Error message is:
Msg 1087, Level 16, State 1, Procedure getFullName, Line 11
Must declare the table variable "#tblName".
Msg 1087, Level 16, State 1, Procedure getFullName, Line 14
Must declare the table variable "#tblName".
Msg 102, Level 15, State 1, Line 1
Incorrect syntax near 'Last_Name'.
Code is:
IF OBJECT_ID (N'dbo.getFullName', N'FN') IS NOT NULL
DROP FUNCTION getFullName
GO
Create function dbo.getFullName(#tblName varchar(30),#fstName varchar(50), #lstName varchar(50) ) returns varchar(101)
As
Begin
Declare #rowCount int
Declare #rowIteration int
Declare #temp varchar(101)
Select #rowCount = count(*) from #tblName
Set #rowIteration = 1
While ( #rowIteration <= #rowCount)
Select #temp = #fstName+' '+#lstName from #tblName where #tblName.Customer_Id = #rowIteration
Begin
Set #rowIteration = #rowIteration + 1
End
return #temp
End
Go
Declare #tblName varchar(30),#fstName varchar(50), #lstName varchar(50)
set #tblName = convert(varchar(30),'dbo.Customer_List')
set #fstName = convert(varchar(50),'dbo.Customer_List.First_Name')
set #lstName = convert(varchar(50),'dbo.Customer_List.Last_Name')
Execute ('select dbo.getFullName('+ #tblName+','+ #fstName+','+ #lstName )
You are essentially trying to perform dynamic sql here without performing it properly. You can't just pass in a variable as a table name, that's why you're getting your error.
You need to recreate this as a stored procedure (or at least you will in sql-server which does not like DML transactions in functions) and use the following dynamic sql:
Declare #sql nvarchar(100)
Set #sql = 'Set #int = (Select count(*) from ' + #tblName + ')'
execute sp_executesql #sql, N'#int int output', #int = #rowCount output

Sum up values from databases and parametrize it. [SQL Server]

I want to sum up values from several databases. At this moment I have three databases: SPA_PROD, SPB_PROD and SPC_PROD.
My SQL query:
IF EXISTS (SELECT *
FROM sys.objects
WHERE object_id = OBJECT_ID(N'[dbo].[TESTSUM]')
AND TYPE IN (N'P',N'PC'))
DROP PROCEDURE [dbo].[TESTSUM]
GO
CREATE PROC TESTSUM
AS
BEGIN
DECLARE #dbName SYSNAME,
#ObjectSUM INT,
#d datetime
SET #d = '20141113'
DECLARE #SQL NVARCHAR(MAX)
DECLARE #DBObjectStats TABLE (
--DBName SYSNAME,
DBObjects INT)
DECLARE curAllDBs CURSOR FOR
SELECT name
FROM MASTER.dbo.sysdatabases
WHERE name like '%PROD'
ORDER BY name
OPEN curAllDBs
FETCH curAllDBs INTO #dbName
WHILE (##FETCH_STATUS = 0) -- db loop
BEGIN
--SQL QUERY
SET #SQL = 'select #dbObjects = sum(doctotal) from ' +
QuoteName(#dbName) + '..Invoice
where DocDate = ''' + cast(#d as varchar(25)) + ''''
PRINT #SQL -- Debugging
EXEC sp_executesql #SQL, N'#dbObjects int output',
#dbObjects = #ObjectSUM output
INSERT #DBObjectStats
SELECT #ObjecSUM
FETCH curAllDBs INTO #dbName
END
CLOSE curAllDBs
DEALLOCATE curAllDBs
-- Return results
SELECT sum(DBObjects) [InvoiceSUM] FROM #DBObjectStats
END
GO
-- Execute stored procedure
EXEC TESTSUM
GO
And this work perfect and giving me right sum from all my DBs: 120 000$ ( 25 000 from SPA_PROD , 95 000 SPC_PROD and 0 (NULL) from SPB_PROD.
What I want to do:
I would like to parametrize, which allows me to choose date and databases. For example I want to choose SPA_PROD and SPB_PROD with date 2014-01-01 in another case I want all databases (SPA + SPB + SPC with another date.
Is this even possible? Any ideas?
I can use everything what gives me SQL Server 2012 and T-SQL. Maybe this technology offers me easiest way to do this.
I am also using SAP Crystal Reports to convert SQL output into a beautiful report.
Sorry for my English and I tried to describe to you my problem as far as I could. If you want any additional information which helps u to help me -> ask me :).
You can create a User-Defined Table Type:
CREATE TYPE DBTable AS TABLE
(
DBName VARCHAR(128)
);
You can use it as an input parameter of your stored procedure. As well as the date parameter.
CREATE PROCEDURE TESTSUM
#Databases DBTable READONLY
,#Date DATETIME
AS
BEGIN
...
...
...
You call it like this:
DECLARE #T AS DBTable;
DECLARE #D AS DATETIME = GETDATE();
INSERT INTO #T VALUES ('DB1', 'DB2', 'DB3')
EXEC TESTSUM #T, #D
maybe instead of
SELECT name
FROM MASTER.dbo.sysdatabases
use
SELECT name
FROM #temptable
and insert into #temptable specific db you want
Using your example I modified it to accept a string of database names (generated through you crystal reports select action). Then passing this string with the date in question to first validate the database exist and if online add the required union clause to the generated SQL code.
CREATE PROCEDURE TESTSUM
#DbNameS NVARCHAR(max)
,#Date DATETIME
AS
BEGIN
DECLARE #SQL NVARCHAR(MAX) = ''
/* ADD EXTRA ',' RO STRING ARRAY OF DATABASES */
SET #DbNameS = #DbNameS + ',';
DECLARE #L INT = LEN(#DbNameS);
DECLARE #D INT = 0;
DECLARE #LD INT = 1;
DECLARE #DBF VARCHAR(50);
DECLARE #ACTIVE INT = 0;
/* START SQL QUERY */
SET #SQL = 'SELECT SUM([InvoiceSUM]) AS [InvoiceSUM] FROM ( SELECT '''' AS DB, 0.00 AS [InvoiceSUM]' + CHAR(13)
/* LOOP THROUGH EACH DBF NAME PASSED CHECKING IF VALID AND ONLINE */
WHILE #D < #L
BEGIN
SET #D = CHARINDEX(',', #DbNameS,#LD);
IF #LD != #D
BEGIN
SET #DBF = SUBSTRING(#DbNameS,#LD,#D-#LD)
/* VALIDATE DBF IS VALID AND ACTIVE */
SELECT #ACTIVE = COUNT(*) FROM SYS.databases WHERE name = #DBF AND [state] = 0
IF #ACTIVE = 1
BEGIN
/*
BEGIN CODE TO UNION THE SUM RESULTS FOR EACH ACTIVE AND VALID DBF
TO MAKE IT WORK WITH SOME EXISTING DBF's ON MY SYSTEM I CHANGED THE SUMMARY CODE FOR TESTING
*/
SET #SQL = #SQL + 'UNION SELECT '''+ #DBF +''' AS DB, ISNULL(SUM( CAST(DVE AS DECIMAL(18,10)) ),0) AS [InvoiceSUM] FROM '+ #DBF + '.DBO.SO_MSTR WHERE CAST(RecordCreated AS DATE) = '''+ CAST(#Date AS VARCHAR(20)) + '''' + CHAR(13)
END;
END;
SET #LD = #D + 1;
END;
/* CLOSE OUT UNION SUMMARY QUERY */
SET #SQL = #SQL + ') AS DATA'
/* OUTPUT RESULTS */
EXEC SP_EXECUTESQL #SQL
END;
Crystal reports would effective be generating this code: EXEC TESTSUM 'SPA_PROD,SPB_PROD,SPC_PROD','12/09/2014'

Stored Procedure to import data into SQL Server database... Error

I have a text file file1.txt in the below format. Column 1 is AGUSR1 & Column 2 is AGUSR2. There are 3 records in the file:
"AGUSR1"|"AGUSR2"
"JASON "|"DEBBIE "
"JOY "|"JASON "
"ANNA "|"JOHN "
I have written a stored procedure to upload this text file into a SQL Server database like this:
CREATE PROCEDURE sp_Import
#TableName varchar(200),
#FilePath varchar(200)
AS
DECLARE #SQL varchar(5000)
SET #SQL = 'BULK INSERT ' + #TableName + ' FROM ''' + #FilePath +
''' WITH (FIELDTERMINATOR = ''|'', ROWTERMINATOR = ''{CR}{LF}'')'
EXEC (#SQL)
To execute it, I have used this statement:
EXEC sp_Import '[DB_DEMO].[dbo].[file1]' , '\\kacl1tsp048\DEMO\file1.txt'
Note : kacl1tsp048 is a remote server the input text file is at.
On execution, I am getting the below error -
Msg 4863, Level 16, State 1, Line 1
Bulk load data conversion error (truncation) for row 1, column 2 (AGUSR2).
The import into table schema is
CREATE TABLE [dbo].[file1]
(
[AGUSR1] [varchar](10) NULL,
[AGUSR2] [varchar](10) NULL
)
For ad-hoc style data imports I sometimes dispense with using BULK INSERTS in favor of selecting from the file itself to then process it. You could do something similar by creating a procedure to read your file as a table:
CREATE FUNCTION [dbo].[uftReadfileAsTable]
(
#Path VARCHAR(255),
#Filename VARCHAR(100)
)
RETURNS
#File TABLE
(
[LineNo] int identity(1,1),
line varchar(8000))
AS
BEGIN
DECLARE #objFileSystem int
,#objTextStream int,
#objErrorObject int,
#strErrorMessage Varchar(1000),
#Command varchar(1000),
#hr int,
#String VARCHAR(8000),
#YesOrNo INT
select #strErrorMessage='opening the File System Object'
EXECUTE #hr = sp_OACreate 'Scripting.FileSystemObject' , #objFileSystem OUT
if #HR=0 Select #objErrorObject=#objFileSystem, #strErrorMessage='Opening file "'+#path+'\'+#filename+'"',#command=#path+'\'+#filename
if #HR=0 execute #hr = sp_OAMethod #objFileSystem , 'OpenTextFile'
, #objTextStream OUT, #command,1,false,0--for reading, FormatASCII
WHILE #hr=0
BEGIN
if #HR=0 Select #objErrorObject=#objTextStream,
#strErrorMessage='finding out if there is more to read in "'+#filename+'"'
if #HR=0 execute #hr = sp_OAGetProperty #objTextStream, 'AtEndOfStream', #YesOrNo OUTPUT
IF #YesOrNo<>0 break
if #HR=0 Select #objErrorObject=#objTextStream,
#strErrorMessage='reading from the output file "'+#filename+'"'
if #HR=0 execute #hr = sp_OAMethod #objTextStream, 'Readline', #String OUTPUT
INSERT INTO #file(line) SELECT #String
END
if #HR=0 Select #objErrorObject=#objTextStream,
#strErrorMessage='closing the output file "'+#filename+'"'
if #HR=0 execute #hr = sp_OAMethod #objTextStream, 'Close'
if #hr<>0
begin
Declare
#Source varchar(255),
#Description Varchar(255),
#Helpfile Varchar(255),
#HelpID int
EXECUTE sp_OAGetErrorInfo #objErrorObject,
#source output,#Description output,#Helpfile output,#HelpID output
Select #strErrorMessage='Error whilst '
+coalesce(#strErrorMessage,'doing something')
+', '+coalesce(#Description,'')
insert into #File(line) select #strErrorMessage
end
EXECUTE sp_OADestroy #objTextStream
-- Fill the table variable with the rows for your result set
RETURN
END
Now you have a proc to convert your file to a table. You would still have to deal with the formatting of your delimiters so you could run something like this to populate [dbo].[file1]:
;WITH Split_Names (Value,Name, xmlname)
AS
(
SELECT
[LineNo],
line,
CONVERT(XML,'<Lines><line>'
+ REPLACE(line,'"|"', '</line><line>') + '</line></Lines>') AS xmlname
from [dbo].[uftReadfileAsTable]('\\kacl1tsp048\DEMO\file1.txt')
where line not like '"AGUSR1"%'
)
SELECT
Value,
RTRIM(REPLACE(xmlname.value('/Lines[1]/line[1]','varchar(100)'),'"', '')) AS AGUSR1,
RTRIM(REPLACE(xmlname.value('/Lines[1]/line[2]','varchar(100)'),'"', '')) AS AGUSR2
INTO [dbo].[file1]
FROM Split_Names
Hope that helps a little?!

Passing two values to a stored procedure?

I've written a stored procedure which is called on a link which provides a date value every time and #cg is NULL that time to filter the result on a particular date.
DECLARE #return_value int
EXEC #return_value = [dbo].[Get_Mydata]
#cg = NULL,
#tosearch = '15-05-2014'
SELECT 'Return Value' = #return_value
GO
And after first execution of the stored procedure, it gives some results and using same stored procedure.
I need to filter result by passing below parameter so this time #cg is NOT NULL.
DECLARE #return_value int
EXEC #return_value = [dbo].[Get_Mydata]
#cg = 'CUSTOMER NAME',
#tosearch = 'manish'
SELECT 'Return Value' = #return_value
GO
I'm not able to figure how should I create a dynamic where clause and add it to existing query as well as how to pass value to same parameter which already been passed as date.
More like first getting results for a particular date and then applying like filter on that result. I cannot pass different parameter that's Front end developers requirement.
This is my stored procedure and table data here. http://sqlfiddle.com/#!3/bb917
create proc Get_Mydata
(
#cg varchar(50),
#tosearch varchar(50)
)
as
begin
set nocount on
declare #sqlquery nvarchar(max)
set #sqlquery = N'select q_no, trandate, cust_name from testsp where CONVERT(Date, trandate, 103) = CONVERT(Date, ''' + #tosearch + ''' ,103)';
create table #temp1
(
q_no int,
trandate datetime,
cust_name varchar(50)
)
insert into #temp1(q_no, trandate, cust_name)
exec (#sqlquery)
select * from #temp1 as T;
set nocount off
end
What I have understood is that you want stored procedure to filter results on Date column when you pass null to #cg param and you want to filter results on Cust_name when you pass string 'Cust_Name' to your #Cg Param.
It should be fairly simple, But in any case you do not need a temp table to get the results back its just an over kill of a fairly simple query.
I would do something like this....
Pass the column name to #ColumnName Parameter, and your value to #tosearch parameter. It will build the query depending on what values you pass.
Make sure when you pass a value(Column Name) to #ColumnName.
create proc Get_Mydata
(
#ColumnName varchar(50),
#tosearch varchar(50)
)
as
begin
set nocount on;
declare #sqlquery nvarchar(max);
set #sqlquery = N' select q_no, trandate, cust_name '
+ N' from testsp '
+ N' where ' + QUOTENAME(#ColumnName) + N' = '
+ CASE
WHEN #ColumnName = 'trandate'
THEN N' CAST(#tosearch AS DATE)'
WHEN #ColumnName = 'cust_name'
THEN N' #tosearch'
ELSE N'' END
EXECUTE sp_executesql #sqlquery
,N'#tosearch varchar(50)'
,#tosearch
set nocount off;
end