I built a wrapper function that currently just calls another table-valued Function but it just added a huge amount to the execution time as Client processing time. Is there a faster way to do this?
Without wrapper:
With Wrapper:
Wrapper function:
CREATE FUNCTION [console].[getCalculosRequisita]
(
#Disponivel BIGINT,
#mediaDiaria float,
#DiasStockArtigo INT,
#DiasAntes INT,
#SaidasPorMes float,
#QtdEncomendada2Meses BIGINT,
#StockAtual BIGINT,
#QtdRequisitada BIGINT,
#caixaMinima INT
)
RETURNS #tbl TABLE
(
DiasAteRotura INT,
AcaoRequisita varchar(10),
Aconselhada BIGINT
)
AS
BEGIN
--future configuration check
--future log input
INSERT INTO #tbl SELECT DiasAteRotura, AcaoRequisita,Aconselhada
FROM [cartridge].[getCalculosRequisitaTSQL]
(
#Disponivel ,
#mediaDiaria ,
#DiasStockArtigo ,
#DiasAntes ,
#SaidasPorMes ,
#QtdEncomendada2Meses ,
#StockAtual ,
#QtdRequisitada ,
#caixaMinima
)
--future log output
RETURN
END
GO
Do it as an inline TVF, which is much, much faster:
CREATE FUNCTION [console].[getCalculosRequisita]
(
#Disponivel BIGINT,
#mediaDiaria float,
#DiasStockArtigo INT,
#DiasAntes INT,
#SaidasPorMes float,
#QtdEncomendada2Meses BIGINT,
#StockAtual BIGINT,
#QtdRequisitada BIGINT,
#caixaMinima INT
)
RETURNS TABLE -- WITH SCHEMABINDING -- preferable, but then you can't change the underlying function
(
DiasAteRotura INT,
AcaoRequisita varchar(10),
Aconselhada BIGINT
)
AS RETURN
(SELECT DiasAteRotura, AcaoRequisita, Aconselhada
FROM [cartridge].[getCalculosRequisitaTSQL]
(
#Disponivel ,
#mediaDiaria ,
#DiasStockArtigo ,
#DiasAntes ,
#SaidasPorMes ,
#QtdEncomendada2Meses ,
#StockAtual ,
#QtdRequisitada ,
#caixaMinima
) AS t
);
GO
Obviously, if you do this then you cannot do any other inserts. In any case logging would be impossible, so I'm not sure what you were planning on doing.
You have not given the code for the underlying function. Perhaps that can be done as an iTVF also.
I really appreciate the replies. They were insightfull and I will upvote them all. I just post some code here for the sharing. Happy to hear your thougts if you have suggestions.
What I really wanted was to run Python from a View, I had to go complety for another direction and I think performance just went down the drain.
It seems Python runs only from Stored Procedures... so had to go for a OPENROWSET (!?) to have it as a VIEW. Not pretty, example below:
DROP PROC IF EXISTS PythonExample;
GO
CREATE PROC PythonExample
AS
BEGIN
set nocount on
SET FMTONLY OFF
DROP TABLE [dbo].[MyRows]
CREATE TABLE [dbo].[MyRows](
[RowNum] [int] NULL
) ON [PRIMARY]
INSERT INTO [MyRows]
([RowNum])
VALUES
(1),
(2)
DECLARE #tbl1 TABLE
(
COL1 INT,
COL2 INT
)
INSERT INTO #tbl1
---ATTENTION Identing is important for Python code...
EXEC sp_execute_external_script #language =N'Python',
#script=N'
import pandas as pd
df= MyInput
df["newCol"]= df["RowNum"]*2
MyOutput = df;
',
#input_data_1_name = N'MyInput',
#input_data_1 =N'SELECT [RowNum] FROM [MyRows]', --- it seems it cannot handle a temp table
#output_data_1_name =N'MyOutput'
--WITH RESULT SETS ((MyColName int, MyColName2 int));
SELECT * FROM #tbl1
END;
GO
DROP VIEW IF EXISTS ViewExample;
GO
CREATE VIEW ViewExample
AS
SELECT * FROM OPENROWSET('SQLNCLI', 'Server=localhost;Trusted_Connection=yes;', 'exec [dbo].[PythonExample]')
GO
SELECT * FROM ViewExample
Related
I have 800+ functions in my database. I would need to modify their source databases dynamically and create snapshots.
example of the function:
create function [schema1].[funTest1] (#param1 varchar(50))
returns table as
return
(
select * from [curr_database1].[schema1].[funTest1](#param1)
union
select * from [curr_database2].[schema1].[funTest1](#param1)
)
I want to change the script as:
create or alter function [schema1].[funTest1] (#param1 varchar(50))
returns table as return
(
select * from [new_database2].[schema1].[funTest1](#param1)
union
select * from [new_database3].[schema1].[funTest1](#param1)
)
basically, I got all the functions script using the sys.syscomments. I'm looking for an option to find and replace the database dynamically to create the snapshots.
How can I get it? Thank you!
Here is the sample code that I have developed for sharing. All the database in the functions starts with the same text(for ex. "curr"). Please share your thoughts. Thanks in advance!
create or alter proc test_proc as
begin
set nocount on
-- this piece of code has the new databases
if object_id('tempdb..#dbNames') is not null drop table #dbNames
create table #dbNames (dbName varchar(1000), id int)
insert into #dbNames(dbName, id) values ('new_database2', 1),('new_database3', 2)
insert into #dbNames(dbName, id) values ('new_database8', 3),('new_database9', 4)
-- this one has the sample functions
if object_id('tempdb..#dbFunctions') is not null drop table #dbFunctions
create table #dbFunctions (funText nvarchar(max))
insert into #dbFunctions (funText) values('create function [schema1].[funTest1] (#param1 varchar(50))
returns table as
return
(
select * from [curr_database1].[schema1].[funTest1](#param1)
union
select * from [curr_database2].[schema1].[funTest1](#param1)
)'),
('create function [schema2].[funTest2] (#param1 varchar(50), #param2 varchar(100))
returns table as
return
(
select * from [curr_database4].[schema2].[funTest2](#param1, #param2)
union
select * from [curr_database5].[schema2].[funTest2](#param1, #param2)
)')
-- declare variables and assign value for #frmStr variable (for testing purposes)
declare #str nvarchar(max)
declare #dbName varchar(100)
declare #frmStr varchar(100) = '[curr_database1]'
-- get the total count of the databases and the functions to iterate and replace the string
declare #dbCnt int = (select count(id) from #dbNames)
declare #fnCnt int = (select count(*) from #dbFunctions)
while #dbCnt > 0
begin
set #dbname = (select dbname from #dbnames where id = #dbcnt)
while #fnCnt > 0
begin
-- this is where I would need to replace the code
select #str = replace(funText, #frmStr, #dbName) from #dbFunctions
select #str
set #fnCnt = #fnCnt - 1
end
set #dbCnt = #dbCnt - 1
end
end
Your actual goal isn't clear, but to answer the question you asked, you can use REPLACE functions in the query to syscomments that you used to get the code in the first place:
REPLACE(
REPLACE([FunctionTextColumn],'curr_database1','new_database2')
,'curr_database2','new_database3'
)
I'm playing around with a query from a software partner that i did not create. I'm trying to use it as a function that i can insert into a table, since i'm no sql expert i'm having difficulties with the sintax.
Thanks to your suggestions i'm making progress, now i'm stuck on how to add an additional select to include some join tables, i have disabled that part converting it to a comment. This actually works without the final select but i need the final select, any ideas?
alter function Renetest
(
#companytest nvarchar(8),
#fiscalyeartest int
)
returns #PERIODBALANCE TABLE
(
[Company] NVARCHAR(8)
,[BookID] NVARCHAR(12)
,[BalanceAcct] NVARCHAR(200)
,[FiscalYear] INT
,[FiscalPeriod] INT
,[BalanceType] NVARCHAR(1)
,[SegValue1] NVARCHAR(50)
,[SegValue2] NVARCHAR(50)
,[FiscalYearSuffix] NVARCHAR(8)
,[FiscalCalendarID] NVARCHAR(12)
)
as
begin
Declare #company nvarchar(8);
Declare #fiscalyear INT;
DECLARE #FiscalPeriod INT;
Set #company = 'epic03'
set #Fiscalyear = '2013'
SET #FiscalPeriod=0;
DECLARE #MaxPeriod AS NVARCHAR(20);
SET #MaxPeriod=(
SELECT
MAX([Erp].[GLBookPer].[FiscalPeriod])
FROM
[Erp].[GLBookPer] WITH (NOLOCK)
WHERE
[Erp].[GLBookPer].[Company] IN (#company)
AND [Erp].[GLBookPer].[FiscalYear]=#FiscalYear
);
WHILE #FiscalPeriod<=(#MaxPeriod)
BEGIN
INSERT INTO #PERIODBALANCE
(
[Company]
,[BookID]
,[BalanceAcct]
,[FiscalYear]
,[FiscalPeriod]
,[BalanceType]
,[SegValue1]
,[SegValue2]
,[FiscalYearSuffix]
,[FiscalCalendarID]
)
SELECT
[Erp].[GLPeriodBal].[Company]
,[Erp].[GLPeriodBal].[BookID]
,[Erp].[GLPeriodBal].[BalanceAcct]
,[Erp].[GLPeriodBal].[FiscalYear]
,#FiscalPeriod AS [FiscalPeriod]
,[Erp].[GLPeriodBal].[BalanceType]
,[Erp].[GLPeriodBal].[SegValue1]
,[Erp].[GLPeriodBal].[SegValue2]
,[Erp].[GLPeriodBal].[FiscalYearSuffix]
,[Erp].[GLPeriodBal].[FiscalCalendarID]
FROM
[Erp].[GLPeriodBal] WITH (NOLOCK)
WHERE
[Erp].[GLPeriodBal].[Company] IN (#company)
AND [Erp].[GLPeriodBal].[FiscalYear]=#FiscalYear
AND [Erp].[GLPeriodBal].[FiscalPeriod]<=#FiscalPeriod
AND [Erp].[GLPeriodBal].[BalanceType] IN ('D','B')
SET #FiscalPeriod=#FiscalPeriod+1;
end;
/*
SELECT
LTRIM(RTRIM([PERIODBALANCE].[Company])) AS [Company]
,LTRIM(RTRIM([PERIODBALANCE].[BookID])) AS [BookID]
,LTRIM(RTRIM(REPLACE([PERIODBALANCE].[BalanceAcct],'|','-'))) AS [BalanceAcct]
,LTRIM(RTRIM(ISNULL(NULLIF([PERIODBALANCE].[BalanceType],''),'--'))) AS [BalanceType]
,LTRIM(RTRIM(ISNULL(NULLIF([PERIODBALANCE].[FiscalYearSuffix],''),'--'))) AS [FiscalYearSuffix]
,LTRIM(RTRIM(ISNULL(NULLIF([PERIODBALANCE].[FiscalCalendarID],''),'--'))) AS [FiscalCalendarID]
FROM
#PERIODBALANCE AS [PERIODBALANCE]*/
return
end
go
The function returns a table. Just select from the function with an INSERT statement.
INSERT INTO dbo.MyTable
SELECT Company, BookID
FROM Renetest();
This is strictly what you asked. But this could be converted into a stored procedure and avoid using a function all together.
I've created the following stored procedure:
ALTER PROCEDURE [dbo].[CountInJunction]
#Mod as nvarchar(10),
#Junction as nvarchar(10),
#PJ as nvarchar(10),
**#case as varchar(10)**,
#Date as varchar(20)
as
begin
declare #result as int
select #result = count(distinct CONCAT ([UCID],[CALLSEGMENT]))
from IVR_LINES
where MODULE = #Mod and DATE = #date
and EVENT_NAME = #Junction and **EVENT_VALUE in (#case)**
insert into [dbo].[MainJuncTable] values(#Mod,#PJ,#Junction,#case,#result,null,null,#date)
return #result
end
I would like to pass ('0','5') as #case.
for some reason, I get 0 as a result, which is not correct. Its seems that the SP doesn't interpret ('0','5') correctly.
I've been trying multiple combinations such as:
'0','5'
'0'+','+5''
'0,5'
etc..
nothing works.
Is there any way I can pass these chars correctly?
Thanks.
Send the values as a single string like ('0,5')
Then in where condition u need to split and select the values like,
where EVENT_VALUE in (select val from Split(#case,','))
Split is user defined function,you need to create before using it.
CREATE FUNCTION [dbo].[Split]
(
#delimited nvarchar(max),
#delimiter nvarchar(100)
) RETURNS #t TABLE
(
-- Id column can be commented out, not required for sql splitting string
id int identity(1,1), -- I use this column for numbering splitted parts
val nvarchar(max)
)
AS
BEGIN
declare #xml xml
set #xml = N'<root><r>' + replace(#delimited,#delimiter,'</r><r>') + '</r></root>'
insert into #t(val)
select
r.value('.','varchar(max)') as item
from #xml.nodes('//root/r') as records(r)
RETURN
END
GO
In every case, use this as your parameter value: '0,5'
But how to use it depends on the version of sql server you're using.
If you've got 2016, there's STRING_SPLIT. https://msdn.microsoft.com/en-us/library/mt684588.aspx
If you don't have it, you can create a function. See related stackoverflow posts: How to split a comma-separated value to columns
Or if you want rows: SQL query to split column data into rows
(See the higher rated recommendations in both of those.)
I created a stored procedure which works perfectly. In an effort to convert it to a function in order to output tables for being used in other code, I created a multi-statement function.
I cannot use temporary tables so i now declare them as table variables but i get the errors bellow. I tried everything i can imagine and searched but either i am not doing it right or its not supported.
Declaring 3 tables as variables (instead of temporary tables in procedure)
I keep getting this error:
Msg 137, Level 16, State 1, Procedure _SLA_report_funct, Line ...
Must declare the scalar variable "#SupCarsSche".
CODE below:
USE DatabaseName
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author: xxxxxxxxxxx
-- Create date:
-- Description:
-- =============================================
ALTER FUNCTION dbo._SLA_report_funct
(
-- Add the parameters for the function here
#Interval varchar(20),
#Starting_Datetime_offset int = 0,
#Trip_or_Day varchar(4)
)
RETURNS
#Table_Var TABLE
(
-- Add the column definitions for the TABLE variable here
TrainNumber int,
SerDate datetime,
First varchar(50),
Last varchar(50),
CusCarNumber int,
Fleet varchar(5),
Scheduled_Car bigint,
Depart varchar(50),
DepartTime varchar(50),
Arrive varchar(50),
ArriveTime varchar(50),
NodeOD int,
Caption nvarchar(255),
Trip_Availability real,
Trp_Availability_Count real,
Duration int
)
AS
BEGIN
-- Fill the table variable with the rows for your result set
DECLARE #SupCarsSche table
(
TrainNumber int,
SerDate datetime,
First varchar(50),
Last varchar(50),
CusCarNumber int,
Fleet varchar(5),
Scheduled_Car bigint,
Depart varchar(50),
DepartTime varchar(50),
Arrive varchar(50),
ArriveTime varchar(50)
)
DECLARE #SQLTemp varchar(MAX)
INSERT #SupCarsSche
SELECT
vsch.TrainNumber,
vsch.SerDate,
vsch.First,
vsch.Last,
vsch. CusCarNumber,
vsch.Fleet,
vsch.CarNumber AS Scheduled_Car,
vsch.Depart,
vsch.DepartTime,
vsch.Arrive,
vsch.ArriveTime
FROM _viaschedule5 AS vsch
RIGHT JOIN _supported_cars as supc ON vsch.CarNumber=supc.CarNumber
WHERE
((#Interval='MONTH') AND (datediff(MONTH, CAST(vsch.SerDate as DATE),(CAST(GETDATE() as DATE)))=#Starting_Datetime_offset) AND
(vsch.CarNumber LIKE '73%' OR NOT (vsch.CusCarNumber='')))
OR
((#Interval='DAY') AND (datediff(DAY, CAST(vsch.SerDate as DATE),(CAST(GETDATE() as DATE)))=#Starting_Datetime_offset) AND
(vsch.CarNumber LIKE '73%' OR NOT (vsch.CusCarNumber='')))
DECLARE #TripAvail table
(
NodeID int,
Caption nvarchar(255),
Trip_Availability real,
Trp_Availability_Count real,
SerDate datetime,
TrainNumber int,
Scheduled_Car bigint,
DepartTime varchar(50),
ArriveTime varchar(50),
Duration int
)
INSERT #TripAvail
SELECT
Nodes.NodeID,
Nodes.Caption AS Caption,
AVG(ResponseTime_Detail.Availability) AS Trip_Availability,
COUNT(ResponseTime_Detail.Availability) AS Trp_Availability_Count,
#SupCarsSche.SerDate,
#SupCarsSche.TrainNumber,
#SupCarsSche.Scheduled_Car,
#SupCarsSche.DepartTime,
#SupCarsSche.ArriveTime,
CASE
WHEN
(Datediff(MINUTE,CAST(#SupCarsSche.DepartTime as TIME),CAST(#SupCarsSche.ArriveTime as TIME))>0)
THEN
Datediff(MINUTE,CAST(#SupCarsSche.DepartTime as TIME),CAST(#SupCarsSche.ArriveTime as TIME))
ELSE
CAST ((24*60)-(DATEPART(hour, #SupCarsSche.DepartTime) * 60) - (DATEPART(minute, #SupCarsSche.DepartTime) * 1)+
(DATEPART(hour, #SupCarsSche.ArriveTime) * 60) + (DATEPART(minute, #SupCarsSche.ArriveTime) * 1)AS INT)
END AS Duration
FROM #SupCarsSche
LEFT JOIN Nodes ON (#SupCarsSche.Scheduled_Car = Nodes.Coach_ID)
LEFT JOIN ResponseTime_Detail ON ((Nodes.NodeID=ResponseTime_Detail.NodeID) AND (
((ResponseTime_Detail.DateTime BETWEEN (#SupCarsSche.SerDate+#SupCarsSche.DepartTime) AND (#SupCarsSche.SerDate+#SupCarsSche.ArriveTime))AND #SupCarsSche.DepartTime<#SupCarsSche.ArriveTime) OR
((ResponseTime_Detail.DateTime BETWEEN (#SupCarsSche.SerDate+#SupCarsSche.DepartTime) AND (DATEADD(DAY,1,#SupCarsSche.SerDate+#SupCarsSche.ArriveTime)))AND #SupCarsSche.DepartTime>#SupCarsSche.ArriveTime )))
GROUP BY #SupCarsSche.TrainNumber,Nodes.NodeID,#SupCarsSche.SerDate,#SupCarsSche.Scheduled_Car,Nodes.Caption,
#SupCarsSche.DepartTime, #SupCarsSche.ArriveTime,
CASE WHEN (
Datediff(MINUTE,CAST(#SupCarsSche.DepartTime as TIME),CAST(#SupCarsSche.ArriveTime as TIME))>0)
THEN
Datediff(MINUTE,CAST(#SupCarsSche.DepartTime as TIME),CAST(#SupCarsSche.ArriveTime as TIME))
ELSE
CAST ((24* 60)-(DATEPART(hour, #SupCarsSche.DepartTime) * 60) - (DATEPART(minute, #SupCarsSche.DepartTime) * 1)+(DATEPART(hour, #SupCarsSche.ArriveTime) * 60) + (DATEPART(minute, #SupCarsSche.ArriveTime) * 1)AS INT)
END
RETURN
END
It looks like when I use table variables, I need to add
FROM #SupCarsSche AS SCSH
After that, I use SCSH everywhere instead of the variable name.
This seems to resolve the issue.
It inserted a first row successfully but it's not inserting any other row, though second row has no conflict of primary key
Code in my aspx.cs file:
outputParVal = sqlCmd.Parameters[outputParName].Value;
outparameter in stored procedure is--- "Result"
CREATE PROCEDURE [dbo].[RecruiterProfileInsert]
#CompanyId int,
#CompanyName varchar(200),
#EmailId varchar(50) ,
#Password varchar(20) ,
#ContactNumber varchar(15),
#Website varchar(50),
#CompanyProfile varchar(2000),
#IsVerified bit,
#Result Tinyint OutPut
--#CreatedDate datetime ,
--UpdatedDate datetime
AS
BEGIN
-- Insert statements for procedure here
--check whether #CompanyName already exist or not if exist then return
IF EXISTS(SELECT Top 1 * FROM RecruiterProfile WHERE #CompanyId = LTRIM(RTRIM(#CompanyId)))
BEGIN
SET #Result = 0-- Already Exists
END
ELSE
BEGIN
INSERT INTO RecruiterProfile
(
CompanyId,
CompanyName,
EmailId ,
Password ,
ContactNumber,
Website ,
CompanyProfile ,
IsVerified,
CreatedDate
)
VALUES
(
#CompanyId,
#CompanyName,
#EmailId ,
#Password,
#ContactNumber,
#Website,
#CompanyProfile,
#IsVerified,
GetDate()
)
set #Result =1
return
END
END
This is the problem:
SELECT Top 1 * FROM RecruiterProfile WHERE #CompanyId = LTRIM(RTRIM(#CompanyId))
This inherently makes no sense. You're comparing the variable to itself. Take the # sign out of one of the CompanyId references. The RTrim is unnecessary in SQL Server, and the LTrim doesn't make sense either because the later insert doesn't also LTrim so something is going to go wrong eventually.
Furthermore, inside of an EXISTS clause, TOP makes no sense unless you are using ORDER BY and doing something with the final result. Just do SELECT * inside of EXISTS clauses.
One more thing: if there is high concurrency and users could possibly try to insert the same thing at the same time, your query could still fail on a duplicate key violation.