SQL Server Include user function result into select query - sql

I have this stored function
function GetPrevReading(
#utility int,
#asofdate datetime
) returns decimal(10,5)
This function returns the previous meter reading from the table with the following fieds:
utility - int
date - datetime
reading - numeric(18,4)
When I use select on this table I want to set a date as a parameter and get this from the table:
Utility Previous Reading
(distinct) GetPrevReading(utility from query, #date from parameter)
I want the function GetPrevReading to take parameter 'utility' from the current row.
Is it possible to accompish this with a query or should I make a stored procedure?
For example, this is the table:
Utility Date Reading
1 2013-10-1 105.6
1 2013-11-1 123.72
2 2013-10-1 226.1
2 2013-10-1 238.18
Now, if I set parameter #date to 2013-10-29 I should get this result:
Utility PreviousReading
1 105.6
2 226.1
Here, my function should get #utility=1 and #asofdate='2013-10-29' on the first row and #utility=2 and #asofdate='2013-10-29' on the second one.

Try this out. I fixed some inconsistencies in your data types, and assumed that your last line of sample data really should have had 2013-11-01 as the date. Also, the way that the function is written, it's not getting the previous reading, but the reading on that date.
CREATE TABLE MyTable (
Utility Int,
Date Date,
Reading Decimal(10,5)
);
INSERT INTO MyTable (Utility, Date, Reading)
VALUES
(1,'2013-10-01', 105.60),
(1,'2013-11-01', 123.72),
(2,'2013-10-01', 226.10),
(2,'2013-11-01', 238.18);
CREATE FUNCTION dbo.GetPrevReading(
#utility int,
#asofdate datetime
)
RETURNS Decimal(10,5)
AS
BEGIN
RETURN (
SELECT TOP 1 Reading
FROM MyTable
WHERE Utility = #Utility
AND Date = #asofdate
ORDER BY Date DESC
)
END;
SELECT
Utility
,Date
,dbo.GetPrevReading(Utility, Date)
FROM (
SELECT Utility, Max(Date) Date
FROM MyTable
WHERE Date < '2013-10-29'
GROUP BY Utility
) x;

Am I understanding the question; the function returns for this call
GetPrevReading( 1,2013-10-29)
Returns
1, 105.6
2, 226.1
And you want to join between the function and its results and the underlying table? You can do this in SQL 2005 + using the Apply join
Select
…
From tblUtilityReadings
Cross Apply GetPrevReading(tblUtilityReadings.utility, #date)

Related

To Create a Function to split dates into Year, Month, Date into a separate column in SQL

Trying to create a function to split dateformat of "2018-05-21" to 2018 | 05 | 21 | as three separate columns. Tried creating the function as below but gives me error on "month", "Day". Error says "incorrect syntax near 'month'. Expecting '(' or Select."
CREATE FUNCTION [dbo].[functionname]
(
-- Add the parameters for the function here
#DateFormat AS DATETIME
)
RETURNS VARCHAR (MAX)
AS
BEGIN
RETURN DATEPART(YEAR,#DateFormat),
DATEPART(Month,#DateFormat),
DATEPART(Day,#DateFormat)
END
GO
The problem with your current SQL is that a scalar only returns a single value. You need to use a table value function to get multiple columns.
This is a TVF version which will provide three columns
CREATE FUNCTION [dbo].[FunctionName]
(
#DateFormat AS DATETIME
)
RETURNS TABLE AS RETURN
(
SELECT DATEPART(YEAR,#DateFormat) AS [Year],
DATEPART(Month,#DateFormat) AS [Month],
DATEPART(Day,#DateFormat) AS [Day]
)
Example usage:
DECLARE #dates TABLE (SomeDate DATE)
INSERT INTO #dates SELECT '01/25/2018'
INSERT INTO #dates SELECT '10/01/2008'
SELECT d.*,fn.* FROM #dates d
CROSS APPLY [dbo].[FunctionName](d.SomeDate) fn
And some documentation.
That said, I personally don't like this implementation. I would simply expect the DATEPART statements in the SELECT portion of the SQL. I think the TVF makes it more complicated and doesn't provide any tangible benefits.

Passing multiple values to a parameter of a function in SQL

There is function Getfunctionname(userid, startdate, enddate) to return a table
My question is can I pass a variable with multiple values?
i.e.
getfunctionname(#userid, startdate, enddate)
Where the value of variable #userid is like
1
2
3
4
5
(actually using split function splitting the values from being 1,2,3,4,5 )
If I can please let me know
One way of doing that which I prefer is to make a new user-defined table data type.
CREATE TYPE [dbo].[IdList] AS TABLE(
[Id] [int] NULL
)
Then you can use that data type as one of the parameters
CREATE FUNCTION Getfunctionname
(
#UserIDs dbo.IdList READONLY,
#startdate INT,
#endtdate INT
)
RETURNS #ReturnTable TABLE
(
-- ReturnTable
)
AS
BEGIN
-- Query
RETURN
END
Use the concept of CSV
CREATE FUNCTION [dbo].[uspGetNumbers]
userid,startdate,enddate // define your paramters the way you want
AS
BEGIN
// your code
JOIN dbo.fnSplit(#UserIDs, ',')
END
GO
Example function:
SELECT [dbo].[uspGetNumbers] '1,2,3,4,5', '', ''
I just ran into this, and I used the CROSS APPLY solution from this post:
SQL Server: run function for each row based on the provided row value
To use CROSS APPLY, you would need to first select your values, and then CROSS APPLY. I have not used the split function before, so I don't have the exact syntax,
but if you use it something like:
select #userid, F1.* from split(1,2,3,4,5),
CROSS APPLY getfunctionname(#userid, startdate, enddate) F1

Use an SSRS multi-value parameter in a SQL query without using the IN operator

I have to pass multiple values (eg: Year1, year2, year3, etc.) to the same query, but I cannot use the IN condition as I'm using less than or equal to in most of the cases. Can I do this by passing multiple values through the same parameter without changing the query?
Is it possible to get multiple values from an SSRS parameter and pass them on to the query to get the output as:
Year1 Year2 Year3
Value(output) Value(output) Value(output)
You can pass the multi-value parameter as a comma separated string but your SQL query is going to need updating to handle that CSV string. In order to pass the multi-value parameter as a CSV string you would open the dataset properties and go to the parameters tab. Then set the value of your parameter to this expresssion:
=JOIN(Parameters!MultiValueYearParameter.Value,",")
This will join all of the values in the multivalue parameter together and use a comma as the delimiter. You can then process this using the split function below (or just modify it to work inline in your SQL if you either cannot or don't need to create a separate function to do this).
This blog post on a Split Function for T-SQL using FOR XML shows how to do this without using string parsing or a while loop. String parsing is prone to error and isn't scalable and while loops should just be avoided in SQL whenever possible.
Below I've modified the split function to return a table of DATE values that you can then use in an INNER JOIN to filter your query using whatever operators you like.
--this is the parameter passed from the report
--(the date strings may not be formatted this way. do not try to rely on that)
DECLARE #YearParameter VARCHAR(MAX) = '2014-01-01,2011-12-02,2015-10-22';
--use this to do the xml parsing
declare #xml xml = N'<root><r>' + replace(#YearParameter,',','</r><r>') + '</r></root>'
--create a table variable to store the date values
DECLARE #dateValues TABLE (val DATE);
--parse the xml/csv string and cast the results to a DATE and insert into the table var
INSERT INTO #dateValues
select CAST(r.value('.','varchar(max)') AS DATE) AS val
from #xml.nodes('//root/r') as records(r);
You can then use that table variable to filter your SQL query. I've given an example of how to use it below.
In the example I create a table of rows with a start and end date. Then I filter that table to show only rows where a parameter value is between the start and end date.
DECLARE #testTable TABLE (Descript VARCHAR(25), startDate DATE, endDate DATE);
INSERT INTO #testTable (Descript, startDate, endDate)
VALUES ('row1', '2014-05-01','2014-08-01'), ('row2', '2013-10-01','2014-01-10'), ('row3', '2015-10-01','2015-12-15'),('row4','2013-01-01','2015-01-01'),
--these rows won't appear in the result set
('row5','2010-01-01','2010-06-01'), ('row6','2013-12-25','2014-05-20');
-- get all rows from the test table where a selected parameter value
-- is between the start and end dates.
SELECT *
FROM #testTable AS tbl
WHERE EXISTS (
SELECT *
FROM #dateValues
WHERE val BETWEEN tbl.startDate AND tbl.endDate
);
In SSRS you can build tables and complex solutions.
In the Text Query of report builder here is an example of splitting apart a parameter to get three dates.
BEGIN
/* suppose the inbound pram is a string with 10 places per date '01/01/2010,11/12/2012,05/06/2013' */
/* you could also nip the string apart by each comma... */
DECLARE #YEAR1 DATETIME
DECLARE #YEAR2 DATETIME
DECLARE #YEAR3 DATETIME
SET #YEAR1 = CAST(SUBSTRING(#INBOUNDPARAM, 1, 10) AS DATETIME)
SET #YEAR2 = CAST(SUBSTRING(#INBOUNDPARAM, 12, 10) AS DATETIME)
SET #YEAR3 = CAST(SUBSTRING(#INBOUNDPARAM, 23, 10) AS DATETIME)
SELECT #YEAR1 AS Year1, #YEAR2 AS Year2, #YEAR3 AS Year3
END
Of course the Year of the date is just YEAR(#Year1) = 2010 for example...

UDF Table UDV or Scalar UDF?

I will do my best to make this question better than my last fiasco. I am getting the dreaded >"cannot find either column "dbo" or the user-defined function or aggregate "dbo.PriMonthAvgPrice", or the name is ambiguous.<
I am attempting to find the avg sales price from the previous month. Here is my UDF:
USE [WoodProduction]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER FUNCTION [dbo].[PriMonthAvgPrice]
(
-- Add the parameters for the function here
#endofmonth datetime,
#begofmonth datetime,
#PlantCode varchar
)
RETURNS decimal (10,2)
AS
BEGIN
-- Declare the return variable here
DECLARE #MonthEndAvgPrice decimal (10,2)
-- Add the T-SQL statements to compute the return value here
SELECT #MonthEndAvgPrice =
(
select
sum(Actual_Sales_Dollars/Actual_Volume)
FROM
woodproduction.dbo.plywood_layup_sales pls
WHERE
Production_Date between #begofmonth and #endofmonth
and actual_volume <> 0
and #PlantCode = pls.Plant_Code
)
-- Return the result of the function
RETURN #MonthEndAvgPrice
END
This is my SELECT statement from my query:
SELECT
DISTINCT
P.[Plant_Number]
,p.plant_name
,pls.plant_code
,(pls.[Budget_Realization]) AS 'BR'
,(pls.[Actual_Volume] ) AS 'AV'
,(pls.[Budget_Volume]) AS 'BV'
--,sum (dpb.[Gross_Production_Per_Hr]) AS 'GPB'
,(p.Production_Volume) AS 'PV'
,CASE
WHEN coalesce (pls.[Actual_Volume],0) = 0 and
coalesce (pls.[Actual_Sales_Dollars],0) = 0
THEN 0
ELSE (pls.[Actual_Sales_Dollars]/pls.[Actual_Volume])
END
AS 'AP'
,pls.production_date
,[dbo].[PriMonthAvgPrice](#endofmonth,#begofmonth, pls.plant_code) AS 'PriMoAvgPrice'
My BASIC understanding is that I HAVE created a Scalar Function. From what I've been reading about my error however, This error returns on TVF's. Is this true? I created a SVF prior to this dealing with just determining a prior month end date so it wasn't as involved as this one where I create the query in the UDF.
Do I need to change this to a TVF? And if so, how do I incorporate SELECT * when I have to join multiple tables along with this?
Thanks in advance.
Aaron
You don't show the from clause, but is the database you created the function in part of it?
Does it work if you fully qualify the name (include the database)?
Have you independently tested the function with:
select [dbo].[PriMonthAvgPrice] ('01/01/2011', '02/01/2011', 'test')
Note: of course you would use some actual values that should return a result.
Please run this and tell us the values returned:
SELECT Actual_Sales_Dollars,Actual_Volume, pls.PLant_code
FROM woodproduction.dbo.plywood_layup_sales pls
WHERE Production_Date between '09-01-2011' and '09-30-2011'
and actual_volume <> 0

Convert Parameter Value Form DateTime To Int32

I am having problems with a Stored Procedure I am writing.
I am gathering data from a number of tables, most of which hold a date value, but one holds just a month(int)
I declare the following Parameters at the beginning of my SP.
#FromDate DateTime,
#ToDate DateTime
This works fine for most of my tables, but for the table where I am just requiring the Month from the #FromDate, I run into the following error:
"Failed to convert parameter value form a DateTime to a Int32."
Here is my Select statement for the Problem Table:
SELECT Branch, Discount
FROM MonthlyPromotions
WHERE (Month = DATEPART(mm,#FromDate))
Also, in the MonthlyPromotions Table the Month Field is an Int.
Can anyone help on this ASAP??
Thankyou
To troubleshoot your problem, can you do a
PRINT CAST(CAST(DATEPART(mm, #FromDate) AS INT) AS VARCHAR(50))
RETURN
At the beginning of your SP?
If that doesn't give an error, you can proceed to:
SELECT Branch, Discount
FROM MonthlyPromotions
WHERE Month = CAST(DATEPART(mm,#FromDate) AS INT)
select cast(getdate() as int)