Msg 512, Level 16 Error - sql-server

How can I solve the following problem?
Msg 512, Level 16, State 1, Line 1 Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression. The statement has been terminated.
I try to get the Frequency like:
| Frequency
| 19
| 23
But somehow I can't get more than I value by firing this Query:
SELECT ts.TimeDifference/ ts.EntryAmount as Frequency
FROM
(
SELECT COUNT(*) as EntryAmount,
(
SELECT DATEDIFF(d, t.minimum, t.maximum)
FROM (SELECT max(Date) AS maximum, min(Date) AS minimum FROM Times GROUP BY column1, column2, column3, column4 HAVING COUNT(*) > 1) t
) AS TimeDifference
FROM Times
) ts
Thank you.

Your innermost sub-select is returning more than one result. You are getting the min and max PER combination of columns 1, 2, 3, and 4. Get rid of the sub-select and it'll resolve the issue.
SELECT
DATEDIFF(d, ts.minimum, ts.maximum)/ ts.EntryAmount as Frequency
,ts.column1, ts.column2, ts.column3, ts.column4
FROM
(
SELECT
COUNT(*) as EntryAmount,
max(Date) AS maximum,
min(Date) AS minimum,
column1, column2, column3, column4
FROM Times
GROUP BY column1, column2, column3, column4
HAVING COUNT(*) > 1
) ts

You can use an aggregate function :
SELECT ts.TimeDifference/ ts.EntryAmount as Frequency
FROM
(
SELECT COUNT(*) as EntryAmount,
(
SELECT Max(DATEDIFF(d, t.minimum, t.maximum))
FROM (SELECT max(Date) AS maximum, min(Date) AS minimum FROM Times GROUP BY column1, column2, column3, column4 HAVING COUNT(*) > 1) t
) AS TimeDifference
FROM Times
) ts

Related

How can I find Value for Max from a GROUP BY

I want to group my data by Date and then find "Value" for MAX("SeqNumber"). How can I do this in an aggregate query.
I tried the below, but it gives and error.
Column 'MyTable.SeqNumber' is invalid in the select list because it is
not contained in either an aggregate function or the GROUP BY clause.
Msg 8120, Level 16, State 1, Line 22 Column 'MyTable.Value' is invalid
in the select list because it is not contained in either an aggregate
function or the GROUP BY clause.
Columns:
Date: date
Time: time
Value: float
Code: varchar <<Added in EDIT
SeqNumber: int
Edited query:
SELECT MAX(case [Code] when 'GOLD' then [Value] end) AS BestGold,
MAX(case [Code] when 'SILVER' then [Value] end) as BestSilver
(CASE WHEN [SeqNumber]=MAX([SeqNumber]) THEN [Value] END) AS HIGHEST << Problem
FROM [MyTable]
GROUP BY [Date]
you can achieve this by using using group by and sub query -
select t1.*
from [MyTable] t1 join
( SELECT
MAX([SeqNumber]) as MX_SeqNumber -- you can add your additional columns here
FROM [MyTable]
GROUP BY [Date] ) as t2
on t1.[SeqNumber] = t2.MX_SeqNumber
WITH CTE AS (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY [Date] ORDER BY Sequence DESC) AS RN
FROM YourTable)
SELECT [Value] AS Highest
FROM CTE
WHERE RN = 1;

SQL Server order by case

I want to create a fourth column with this query:
select * from (select ID, column0, column1, column2, column3 = Case when
column2 = '' then column1 else column2 end, row_number() over(partition by
column0 order by [column3]) as column4 from myTable) ti
But this error appears:
Invalid column name 'column3'.
I want this result: (red marked column):
you need to use order by from outside of subquery as below:
Select *, row_number() over(partition by
column3 order by [Id]) as column4
from (select ID, column0, column1, column2, column3 = Case when
column2 = '' then column1 else column2 end
from myTable) ti
For your 4th column in image you need to partition by column3 order by id. If you do Partition by column0 then you will get all 1's
This way it will work:
select *, row_number() over(partition by
column0 order by [column3]) as column4 from
(select ID, column0, column1, column2, column3 = Case when
column2 = '' then column1 else column2 end from myTable) ti

In SQL Server how can I fetch all rows with a date greater than some set date or at least 20 rows

I'm looking for a way to fetch at least 20 rows, regardless of the date or all rows with a date greater than a set date (whichever of the 2 fetches the most rows).
For example:
SELECT *
FROM table1
WHERE the_date >= '2014-11-01'
ORDER BY the_date DESC
will give me what I want, but if this returns less than 20 rows then I want to keep going before that date until I get 20 (or until there are no more rows - whichever comes first).
I could just select all and take the ones I need programatically, but I'm trying to avoid that as that would be a major change to the actual code in many places.
Can this be done?
Note: I am using SQL Server 2008.
Here's an approach:
select *
from
(
select row_number() over (order by the_date desc) num, t.*
from table1 t
) i
where i.the_date >= '2014-11-01'
or i.num < 21
Very strange requirement but something like this should work.
DECLARE #Date date = '2014-11-01';
with MyResults as
(
SELECT *, 1 as RowNum
FROM table1
WHERE (the_date >= #Date )
UNION ALL
SELECT top 10 *, ROW_NUMBER() over(order by the_date desc)
FROM table1
order by the_date desc
)
Select *
from MyResults
where RowNum <= 20
The right answer was almost there, but the user has removed it, so here it is again:
SELECT *
FROM table1
WHERE the_date >= '2014-11-01'
union
SELECT top 20 *
FROM table1
ORDER BY the_date desc
The removed answer had union all
This answer has a problem with duplicate dates , but with the data I have that is not an issue

Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, or when the subquery is used as an expression

I need to update total field of tableA
with the result from the query
select SUM(amt) from SALES group by DATE,RNAME
I tried like this
UPDATE tableA
SET Total = (
SELECT Sum(billamt)
FROM SALES
GROUP BY DATE
,RNAME
)
It shows following error
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
The query:
select sum(billamt) from SALES group by DATE,RNAME
Returns the sum of billamt, grouped by DATE,RNAME.
If you have more than one DATE or RNAME unique values, the returned value is a table (and not one field).
You can run this query alone and check the returned value.
As the error states, you cannot set more than one value to a field.
Try something like:
update A
set Total = B.Total
From tableA A
Inner Join (select DATE,RNAME,sum(billamt) Total from SALES group by DATE,RNAME) B
On A.Date = B.Date and A.RName = B.RNAme
As the table structure is not clear in the above question, I assume that tableA has column Total, Date and RName
update tableA set tableA.total =
a.total from (select SUM(amt) as total, date, rname from SALES group by DATE,RNAME) as a
where a.date = tableA.date and tableA.rname = a.rname
-- Edit for the "case where Rname can be null in sales table" use
update tableA set tableA.total = a.total
from (select SUM(amt) as total, date, rname from #t1 group by DATE,RNAME) as a
where a.date = tableA.date and tableA.rname = a.rname
OR (a.date = tableA.date AND tableA.rname IS NULL and a.rname IS NULL)
-- Further for elaboration
CREATE TABLE #t1 (amt decimal(18,2), [date] date, rname varchar(50))
INSERT INTO #t1
SELECT 1.9, GETDATE() - 1, NULL
UNION ALL
SELECT 1.9, GETDATE() - 1, NULL
UNION ALL
SELECT 8.9, GETDATE() - 1, NULL
UNION ALL
SELECT 8.9, GETDATE(), NULL
UNION ALL
SELECT 2.9, GETDATE() - 1, 'N1'
UNION ALL
SELECT 2.8, GETDATE() - 1, 'N1'
CREATE TABLE #t2 (total decimal(18,2), [date] date, rname varchar(50))
INSERT INTO #t2
SELECT 0, GETDATE() - 1, NULL
UNION ALL
SELECT 0, GETDATE() - 1, 'N1'
update #t2 set #t2.total = a.total
from (select SUM(amt) as total, date, rname from #t1 group by DATE,RNAME) as a
where a.date = #t2.date and #t2.rname = a.rname
OR (a.date = #t2.date AND #t2.rname IS NULL and a.rname IS NULL)
SELECT * FROM #t2
I hope its clear now

How to set the maxrecursion option for a CTE inside a Table-Valued-Function

I'm facing a problem declaring the maxrecursion option for a CTE inside a TVF.
Here is the CTE (a simple calendar):
DECLARE #DEBUT DATE = '1/1/11', #FIN DATE = '1/10/11';
WITH CTE as(
SELECT #debut as jour
UNION ALL
SELECT DATEADD(day, 1, jour)
FROM CTE
WHERE DATEADD(day, 1, jour) <= #fin)
SELECT jour FROM CTE option (maxrecursion 365)
and the TVF:
CREATE FUNCTION [liste_jour]
(#debut date,#fin date)
RETURNS TABLE
AS
RETURN
(
WITH CTE as(
SELECT #debut as jour
UNION ALL
SELECT DATEADD(day, 1, jour)
FROM CTE
WHERE DATEADD(day, 1, jour) <= #fin)
SELECT jour FROM CTE
--option (maxrecursion 365)
)
The above TVF is running OK without the maxrecursion option
but there is a syntax error with the option.
What is the solution?
From this MSDN forums thread I learn that
[the] OPTION clause can be used only at the statement level
So you cannot use it within a query expression inside view definitions or inline TVFs etc. The only way to use it in your case is to create the TVF without the OPTION clause and specify it in the query that uses the TVF. We have a bug that tracks request for allowing use of OPTION clause inside any query expression (for example, if exists() or CTE or view).
and further
You can not change the default value of that option inside a udf. You
will have to do it in the statement referencing the udf.
So in your example, you must specify the OPTION when you call your function:
CREATE FUNCTION [liste_jour]
(#debut date,#fin date)
RETURNS TABLE
AS
RETURN
(
WITH CTE as(
SELECT #debut as jour
UNION ALL
SELECT DATEADD(day, 1, jour)
FROM CTE
WHERE DATEADD(day, 1, jour) <= #fin)
SELECT jour FROM CTE -- no OPTION here
)
(later)
SELECT * FROM [liste_jour] ( #from , #to ) OPTION ( MAXRECURSION 365 )
Note that you can't work round this by having a second TVF that just does the above line - you get the same error, if you try. "[the] OPTION clause can be used only at the statement level", and that's final (for now).
Old thread, I know, but I needed the same thing and just dealt with it by using a multi-statement UDF:
CREATE FUNCTION DatesInRange
(
#DateFrom datetime,
#DateTo datetime
)
RETURNS
#ReturnVal TABLE
(
date datetime
)
AS
BEGIN
with DateTable as (
select dateFrom = #DateFrom
union all
select DateAdd(day, 1, df.dateFrom)
from DateTable df
where df.dateFrom < #DateTo
)
insert into #ReturnVal(date)
select dateFrom
from DateTable option (maxrecursion 32767)
RETURN
END
GO
There are probably efficiency issues with this, but I can afford it in my case.
A little creative use of CTEs and cartesian products (cross joins) will get you around the MAXRECURSION limit of 100. 3 CTEs with a limit of 4 records on the last one nets you 40,000 records, which will be good for more than 100 years worth of data. If you expect more difference between #debut and #fin, you can adjust cte3.
-- please don't SHOUTCASE your SQL anymore... this ain't COBOL
alter function liste_jour(#debut date, #fin date) returns table as
return (
with cte as (
select 0 as seq1
union all
select seq1 + 1
from cte
where seq1 + 1 < 100
),
cte2 as (
select 0 as seq2
union all
select seq2 + 1
from cte2
where seq2 + 1 < 100
),
cte3 as (
select 0 as seq3
union all
select seq3 + 1
from cte3
where seq3 + 1 <= 3 -- increase if 100 years isn't good enough
)
select
dateadd(day, (seq1 + (100 * seq2) + (10000 * seq3)), #debut) as jour
from cte, cte2, cte3
where (seq1 + (100 * seq2) + (10000 * seq3)) <= datediff(day, #debut, #fin)
)
go
-- test it!
select * from liste_jour('1/1/2000', '2/1/2000')
Old issue but... I just wanted to clarify why OPTION(MAXRECURSION x) is not allowed in an in-line table-valued function. This is because iTVF's get inlined when you use them in a query. And, as we all know, you cannot put this option anywhere else save at the very end of the query. This is THE reason it will never be possible to put it inside an iTVF (unless the parser and/or algebrizer does some magic behind the scenes, which I don't think it will any time soon). mTVF's (multi-statement table-valued functions) is a different story because they don't get inlined (and are so slow that they should never be used in queries; it's OK to use them in an assignment to a variable, though, but then again---beware of loops!).
Another way to handle this is to break up the problem into a pair of CTEs, neither of which hits the recursion limit of 100. The first CTE creates a list with the begin date for each month in the range. The second CTE then fills in all the days of each month. As long as the input range is fewer than 100 months, it should work fine. If an input range of greater than 100 months is required, the same idea could be expanded with a third CTE for years added ahead of the months CTE.
CREATE FUNCTION [liste_jour]
(#debut datetime, #fin datetime)
RETURNS TABLE
AS
RETURN
(
WITH CTE_MOIS AS
(
SELECT JOUR_DEBUT = #debut
UNION ALL
SELECT DATEADD(MONTH, 1, CTE_MOIS.JOUR_DEBUT)
FROM CTE_MOIS
WHERE DATEADD(MONTH, 1, CTE_MOIS.JOUR_DEBUT) <= #fin
),
CTE_JOUR AS
(
SELECT JOUR = CTE_MOIS.JOUR_DEBUT
FROM CTE_MOIS
UNION ALL
SELECT DATEADD(DAY, 1, CTE_JOUR.JOUR)
FROM CTE_JOUR
WHERE MONTH(CTE_JOUR.JOUR) = MONTH(DATEADD(DAY, 1, CTE_JOUR.JOUR)) AND
DATEADD(DAY, 1, CTE_JOUR.JOUR) <= #FIN
)
SELECT JOUR
FROM CTE_JOUR
)
create simple sample for you :)
/* block create function for test in sql*/
/*FUNCTION [fn_CTE_withLevel] (#max_level int)
RETURNS TABLE
AS
RETURN
( */
/******************* declare table just replace real table *****/
declare #tbl table(pid varchar(15),id varchar(15))
/* use function argument */
declare #max_level int = 3
Insert Into #tbl(pid , id)
values
/*lev1*/ ('0','1') ,
/*lev2*/ ('1','101') ,
/*lev2*/ ('1','102') ,
/*lev1*/ ('0','2') ,
/*lev2*/ ('2','201') ,
/*lev3*/ ('201','20101') ,
/*lev3*/ ('201','20102') ,
/*lev2*/ ('2','202') ,
/*lev1*/ ('0','3') ,
/*lev2*/ ('3','301') ,
/*lev2*/ ('3','302') ,
/*lev1*/ ('0','4') ,
/*lev2*/ ('4','401'),
/*lev2*/ ('4','402');
/******************* declare table just replace real table *****/
With cte_result(pid , id , lev)
As(
Select pid , id , 1 as lev From #tbl t
Where pid = '0' /* change to another values from list to test sub items */
Union All
Select t.pid , t.id , cte.lev + 1 as lev
From cte_result cte
inner Join #tbl t
On cte.id = t.pid
Where cte.lev < #max_level -- :) this is my idea
)
Select * From cte_result
--OPTION (MAXRECURSION 100)
-- uncomment for create function
/*)*/

Resources