Table-valued function displaying only 1 result - sql-server

I've got this table valued function and I want it to display all records with the same "PeopleID". How can I do it? It's currently displaying only 1 of the possible results.
ALTER FUNCTION dbo.ufnGetContactInformation(#FuncID int)
RETURNS #retContactInformation TABLE
(
-- Columns returned by the function
FuncID int PRIMARY KEY NOT NULL,
Full_Name nvarchar(75) NULL,
Phone nvarchar(100) NULL,
Email nvarchar(50) NULL,
City nvarchar(20) NULL
)
AS
BEGIN
DECLARE
#fullname nvarchar(75),
#phonenumber nvarchar(100),
#email nvarchar(50),
#city nvarchar(20);
-- > Get common contact information
SELECT
#fullname = p.Full_Name,
#phonenumber = c.Phone,
#email = c.Email,
#city = c.CityID
FROM d_People p, d_Contacts c
WHERE p.PeopleID=#FuncID;
-- < Get common contact information
IF #FuncID IS NOT NULL
BEGIN
INSERT #retContactInformation
SELECT #FuncID, #fullname, #phonenumber, #email, #city;
END;
RETURN;
END;
GO

You need a proper join for your two tables, otherwise your query makes no sense. What you have now will give you one person full_name, but every contact phone, email, and city.
Bad habits to kick : using old-style JOINs - Aaron Bertrand
When you select into the scalar variables, you will only get one result in each variable. Inserting the variables after setting them to only hold one row will only ever yield at most one row.
You can simplify your function as an in-line table-valued function like so:
alter function dbo.ufngetcontactinformation(#funcid int)
returns table as return (
select
funcid = p.peopleid
, fullname = p.full_name
, phonenumber = c.phone
, email = c.email
, city = c.cityid
from d_people p, d_contacts c
where p.peopleid = #funcid;
);
go
Reference
When is a SQL function not a function? "If it’s not inline, it’s rubbish." - Rob Farley
Inline Scalar Functions - Itzik Ben-Gan
Scalar functions, inlining, and performance: An entertaining title for a boring post - Adam Machanic
TSQL User-Defined Functions: Ten Questions You Were Too Shy To Ask - Robert Sheldon
If you have to have a multi-statement table valued function despite the perfomance hit:
alter function dbo.ufngetcontactinformation(#funcid int)
returns #retcontactinformation table
(
-- columns returned by the function
funcid int primary key not null,
full_name nvarchar(75) null,
phone nvarchar(100) null,
email nvarchar(50) null,
city nvarchar(20) null
)
as
begin
insert #retcontactinformation
select
#funcid
, p.full_name
, c.phone
, c.email
, c.cityid
from d_people p, d_contacts c
where p.peopleid = #funcid;
return;
end;
go

Related

Wrapper TSQL Table-valued Function is slow

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

How to convert a query to a a function

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.

SQL: Multi statement function with multiple temporary tables (variables)

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.

Select from table when stored procedure parameter empty

As my title suggests, I have a stored procedure query where I pass in a parameter and it returns data based on that parameter. However, if I pass a blank, I would like that everything in the table be returned.
I have posted some code, as you see....the #id parameter selects only those of that ID correctly but I'd like to know what I should add (or remove) so if this parameter is blank everything is return.
Thank you
CREATE PROCEDURE [dbo].[select_stuent]
#id varchar(25)
AS
CREATE TABLE dbo.#usp_holder (name varchar(25),
id char(5),
class varchar (25),
grade numeric(5)
)
INSERT INTO #usp_ holder (name, id, class)
SELECT
name, id, class
FROM
dbo.classmaster
INNER JOIN
dbo.classtypes ON classmaster.type = classtypes.type
WHERE
classmaster.id = #id;
Try this:
CREATE PROCEDURE [dbo].[select_stuent]
#id varchar(25)
AS
CREATE TABLE dbo.#usp_holder
(
name varchar(25),
id char(5) = null,
class varchar (25)
)
You may want to add another condition to check if the input is an empty string but not null. Like this:
IF (#id = '')
BEGIN
SET #id = null
END
INSERT INTO
#usp_ holder (name,id,class
)
Select name,id,class
from dbo.classmaster
inner join dbo.classtypes
on classmaster.type= classtypes.type
WHERE classmaster.id = ISNULL(#id, classmaster.id)

Can't insert a second row into a table though it insert first row by stored procedure

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.

Resources