Is this a SQL Server table-valued function? - sql-server

Can anyone explain this T-SQL statement:
select *
from (select getdate()) as func(param)
You can copy & paste the code, and then run it

There is no table valued function or dynamic SQL at all. It is simple subquery:
inner query: select getdate()
outer query: SELECT * FROM (inner_query) AS func(param)
func - it is just alias for subquery
param - it is alias for column
It could be rewritten as:
select sub.current_date_value
from (select getdate() AS current_date_value) as sub

The inner select query is a derived table. Search for that term to lean more. This is functionally identical (no pun intended) to:
SELECT GETDATE() AS param;
There is no need to use a derived table here.

Related

Table Valued Function with Recursive CTE

Just for fun, I’m trying to write a table valued function to generate a table of dates. For testing purposes, I am hard-coding values which should be passed in variables.
By itself, this works:
WITH cte AS (
SELECT cast('2021-10-01' AS date) AS date
UNION ALL
SELECT dateadd(day,1,date) FROM cte WHERE date<current_timestamp
)
SELECT * FROM cte OPTION(maxrecursion 0);
Note the OPTION at the end.
As a function, it won’t work unless I remove the OPTION clause at the end:
CREATE FUNCTION dates(#start date, #rows INT) RETURNS TABLE AS
RETURN
WITH cte AS (
SELECT cast('2021-10-01' AS date) AS date
UNION ALL
SELECT dateadd(day,1,date) FROM cte WHERE date<current_timestamp
)
SELECT * FROM cte -- OPTION(maxrecursion 0)
;
For the test data, that’s OK, but it will certainly fail if I give it date at the beginning of the year, since it involves more than 100 recursions.
Is there a correct syntax for this, or is it another Microsoft Quirk which needs a workaround?
I think this may be the answer.
In an answer to another question, #GarethD states:
If you think of a view more as a stored subquery than a stored query … and remember that its definition is expanded out into the main query …
(Incorrect Syntax Near Keyword 'OPTION' in CTE Statement).
If that’s the case, a view can’t include anything that can’t be included in a subquery. That includes the ORDER BY clause and hints such as OPTION.
I have yet to make sure, but I can guess that the same goes for a Table Valued Function. If so, the answer is no, there is no way include the OPTION clause.
Note that other DBMSs are more accommodating in what can be included in subqueries, so I don’t imagine that they have the same limitations.
The solution is to use a multi-statement Table Valued Function:
DROP FUNCTION IF EXISTS dates;
GO
CREATE FUNCTION dates(#start date, #end date) RETURNS #dates TABLE(date date) AS
BEGIN
WITH cte AS (
SELECT #start AS date
UNION ALL
SELECT dateadd(day,1,date) FROM cte WHERE date<#end
)
INSERT INTO #dates(date)
SELECT date FROM cte OPTION(MAXRECURSION 0)
RETURN;
END;
GO
SELECT * FROM dates('2020-01-01','2021-01-01');
Inline Table Valued Functions are literally inlined, and clauses such as OPTION can only appear at the very end of an SQL statement, which is not necessarily at the end of the inline function.
On the other hand, a Multi Statement Table Valued function is truly self-contained, so the OPTION clause is OK there.

Datediff last and previous dates SQL

I'm learning SQL, for an exercise I have to several things.
I'm making a query to compare the most recent orderdate with the orderdate before. I want to use a correlated subquery for this. I have already made it using a Cross Apply and Window functions.
At the moment I have this:
select
b1.klantnr,
DATEDIFF(D, (Select MAX(b1.Besteldatum)),
(Select MAX(b1.Besteldatum)
where besteldatum not in (Select MAX(b1.besteldatum)))) as verschil
from
bestelling b1
group by
b1.klantnr, b1.besteldatum
I only get null values in the datediff column. It should return this:
Results
I'm using SQL Server 2014 Management Studio.
Any help appreciated.
Here is one simple way:
select datediff(day, min(bs.Besteldatum), max(bs.Besteldatum)) as most_recent_diff
from (select top (2) bs.*
from bestelling bs
order by bs.Besteldatum
) bs;
This uses a subquery, but not a correlated subquery. Should have really good performance, if you have an index on bestselling(Besteldatum).
A correlated subquery way.
select top 1 bs.*,datediff(day,
(select max(bs1.Besteldatum)
from bestelling bs1
where bs1.Besteldatum<bs.Besteldatum),
bs.Besteldatum
) as diff
from bestelling bs
order by bs.Besteldatum desc
This gives only the difference between latest date and the date preceding it. If you need all records remove top 1 from the query.

SQL Server Aggregate Subquery Error for Condition Referencing Parent Query

I'm trying to add an aggregate sub query in select statement for the following code.
DECLARE #Date1 Date
DECLARE #Date2 Date
SET #Date1 = '2017-01-01'
SET #Date2 = '2017-03-01'
SELECT
p.PracticeName [Practice Name],
dbo.getFormattedName(l.Userid) [User Name],
MAX(EventDate) [Last Activity],
COUNT(*) [Activity Count],
(SELECT COUNT(*)
FROM UserEvent EVT (NOLOCK)
WHERE EVT.EventTypeID = 1
AND EVT.UserID = au.userID
AND EVT.EventDate >= #Date1
AND EVT.EventDate <= DATEADD(DAY, 1, #Date2)
GROUP BY
au.userID) [Login Count]
FROM
dbo.AudLog l (NOLOCK)
JOIN
Appuser au (NOLOCK) ON l.UserID = au.UserID
JOIN
Practice p (NOLOCK) ON au.PracticeID = p.PracticeID
WHERE
l.EnvironmentID = 1
AND EventDate >= #Date1
AND EventDate <= DATEADD(DAY,1,#Date2)
GROUP BY
p.PracticeName,
dbo.getFormattedName(l.Userid)
ORDER BY
p.PracticeName,
dbo.getFormattedName(l.Userid)
I'm getting the following error:
Column 'Appuser.UserID' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
I don't understand why that error applies to my sub query because I'm not selecting AppUser.UserID, I'm just using as a reference for a condition to align the sub query with the parent query. Also, it is indeed in the GROUP BY statement within the sub query.
I referenced this question but based on the explanation I would think my query would be working.
Any help appreciated.
The question you are referencing is different because there are no aggregate functions in the outer query that require a GROUP BY.
When you use a correlated subquery as your have (EVT.UserID = au.userID), the outer query column reference, in this case au.userID, is now a part of the columns in the outer select (because it is necessary to the subquery).
The simple fix is to add the au.userID column to your outer group by.
If you join on a field it is present in the intermediary data set that is part of the join. Even if it is not in your select list you need to consider it as part of the list.
If you really think it is a non-issue then you can add it to your group by clause and still get the results you desire.
The error is not referencing your sub query.
The following line dbo.ch_getFormattedProviderName(l.Userid) in the select clause of your main query is the problem.
You are using the function call dbo.ch_getFormattedProviderName(l.Userid) in your select statement but you are grouping by a different function dbo.getFormattedName(l.Userid) in your group by clause.
Change the function dbo.ch_getFormattedProviderName to dbo.getFormattedName or vice versa.
It is not answer to your question, but. There are some several things I would like to mention in your query.
Avoid using scalar functions. Try not use them at all. They are almost no good examples of usage of them. If you wish to encapsulate the logic then just use Table Valued Functions instead. Scalar functions are performance killers especially if you need to filter by them. They will not be indexed and they will execute on every single row.
Personally I didn't see the situation when such inline sub-queries would outperform normal JOIN inline query. But I saw a lot of opposite situations when the performance was worse. You can always re-write such query with the JOIN (SELECT ...)
NOLOCKs ... they are not very recommended. If you have locking problems then solving them by taking using index/re-design Technics. It is better to solve the root of the problem rather than doing workarounds.

Use of the IN condition

I can easily create a stored procedure in SQL Server with parameters that I use with =, LIKE and most operators. But when it comes to using IN, I don't really understand what to do, and I can't find a good site to teach me.
Example
CREATE PROCEDURE TEST
#Ids --- What type should go here?
AS BEGIN
SELECT * FROM TableA WHERE ID IN ( #Ids )
END
Is this possible and if so how ?
With SQL Server 2008 and above, you can use Table Valued Parameters.
You declare a table type and can use that as a parameter (read only) for stored procedures that can be used in IN clauses.
For the different options, I suggest reading the relevant article for your version of the excellent Arrays and Lists in SQL Server, by Erland Sommarskog.
I've done this in the past using a Split function that I add to my schema functions as described here
Then you can do the following:
CREATE PROCEDURE TEST
#Ids --- What type should go here?
AS BEGIN
SELECT * FROM TableA WHERE ID IN ( dbo.Split(#Ids, ',') )
END
Just remember that the IN function always expects a table of values as a result. SQL Server is smart enough to convert strings to this table format, so long as they are specifically written in the procedure.
Another option in your specific example though, could be to use a join. This will have a performance improvement, but often does not really meet a real-world example you need. The join version would be:
SELECT *
FROM TableA AS ta
INNER JOIN dbo.Split(#Ids, ',') AS ids
ON ta.Id = ids.items
If your asking what I think your asking, I do this every day..
WITH myData(FileNames)
AS
(
SELECT '0608751970%'
UNION ALL SELECT '1000098846%'
UNION ALL SELECT '1000101277%'
UNION ALL SELECT '1000108488%'
)
SELECT DISTINCT f.*
FROM tblFiles (nolock) f
INNER JOIN myData md
ON b.FileNames LIKE md.FileNames
Or if your doing this based on another table:
WITH myData(FileNames)
AS
(
SELECT RTRIM(FileNames) + '%'
FROM tblOldFiles
WHERE Active=1
)
SELECT DISTINCT f.*
FROM tblFiles (nolock) f
INNER JOIN myData md
ON b.FileNames LIKE md.FileNames

Use of table valued function in order by clause

Can I use my table valued function in order by clause of my select query????
Like this :
declare #ID int
set #ID=9011
Exec ('select top 10 * from cs_posts order by ' + (select * from dbo.gettopposter(#ID)) desc)
GetTopPoster(ID) is my table valued function.
Please help me on this.
You can use a table-valued function with a join. That also allows you to choose any combination of columns to sort by:
select top 10 *
from cs_posts p
join dbo.gettopposter(#ID) as gtp
on p.poster_id = gtp.poster_id
order by
gtp.col1
, gtp.col2
Yes. You can use a Table Valued Function just as a normal table.
Your query is not valid SQL though, despite the TVF.
For further reference:
http://msdn.microsoft.com/en-us/library/ms191165.aspx
You can't do it like that - how does it know what to order by? It doesn't know how the TVF relates to the original query. You can join the two however (as I assume cs_posts has an id column which relates to the TVF) and then order by the the TVF id column.

Resources