Error in using aggregate function max() oracle - oracle11g

I am trying to run query
select * from OS_Historystep where step_name = '011' and finish_date = max(finish_date) ;
But i am getting error that
ORA-00934: group function is not allowed here
00934. 00000 - "group function is not allowed here"
*Cause:
*Action:
Error at Line: 12 Column: 72
what i am doing wrong?
Thanks

You can't use an aggregate in a where clause. Also, you can't mix non-aggregated data and aggregated data from the same column in a single select. You'll need to use a sub-query:
select *
from OS_Historystep hs1
where step_name = '011'
and finish_date = (select max(finish_date)
from OS_Historystep hs2
where hs2.step_name = hs1.step_name);

you cannot reference an aggregate like that. you would either have to put a sub query up like below (assuming you wanted the max(finish_date) to mean the max finish date for step 011 and not the max finish date in the whole table (which may return no rows):
select *
from OS_Historystep
where step_name = '011'
and finish_date = (select max(finish_date)
from OS_Historystep
where step_name = '011');
or use an analytic function
select *
from (select s.*, rank() over (partition by step_name order by finish_date desc) rnk
from OS_Historystep s
where step_name = '011')
where rnk = 1;

Related

I need only one unique result in Oracle sdo_nn Update sentence ,

I need Only one unique result from tableB.Field to tableA.Field
I am using sdo operator sdo_nn, this is the code:
UPDATE table1 t1
SET t1.fieldA = (SELECT T2.fieldB,SDO_NN_DISTANCE(1) distance
FROM table1 T1, table2 T2
WHERE
(sdo_nn(t1.geometry,t2.geometry,'SDO_NUM_RES=1',1)= 'TRUE')
ORDER BY DIST
)
WHERE EXISTS(
SELECT 1
FROM table2 t2
WHERE sdo_nn(t1.geometry, t2.geometry,'SDO_NUM_RES=1',1)='TRUE'
AND(t2.cell_name = 'string1' or t2.cell_name = string2')AND t1.fieldA = NULL
);
In the select sentence of the subquery i get an error because i only use one field(t1.fieldA), but in the sentence i use the operator SDO_NN_DISTANCE(1) and the sql developer count this operator like another field. What is the correct way to write this sentence? I only use sql because i need to insert this code in vba
Thanks!!!
Obviously, you can't (simplified)
set t1.fieldA = (t2.fieldB, distance) --> you want to put two values into a single column
Therefore, get fieldB alone from the subquery which uses analytic function (row_number) to "sort" rows by sdo_nn_distance(1) desc; then get the first row's fieldB value.
Something like this (I hope I set the parenthesis right):
UPDATE table1 t1
SET t1.fieldA =
(SELECT x.fieldB --> only fieldB
FROM (SELECT T2.fieldB, --> from your subquery
SDO_NN_DISTANCE (1) distance,
ROW_NUMBER ()
OVER (ORDER BY sdo_nn_distance (1) DESC) rn
FROM table1 T1, table2 T2
WHERE (sdo_nn (t1.geometry,
t2.geometry,
'SDO_NUM_RES=1',
1) = 'TRUE')) x
WHERE rn = 1) --> where RN = 1
WHERE EXISTS
(SELECT 1
FROM table2 t2
WHERE sdo_nn (t1.geometry,
t2.geometry,
'SDO_NUM_RES=1',
1) = 'TRUE'
AND ( t2.cell_name = 'string1'
OR t2.cell_name = 'string2')
AND t1.fieldA IS NULL);

sqlite3 update column with rollling average

I want to update a column with the rolling average of the product of two other columns, all in the one table.
create table tb (code str, date str, cl float, vo float);
insert into tb values
('BHP', '2020-01-03', 3.25, 1000),
('BHP', '2020-01-04', 3.50, 2000),
('BHP', '2020-01-05', 3.55, 1000),
('CSR', '2020-01-03', 5.55, 1500),
('CSR', '2020-01-04', 5.60, 2000),
('CSR', '2020-01-05', 5.55, 2000),
('DDG', '2020-01-03', 10.20, 4000),
('DDG', '2020-01-04', 10.25, 4500),
('DDG', '2020-01-05', 10.30, 5000);
alter table tb add column dv float;
alter table tb add column avg_dv float;
update tb set dv = (select cl * vo);
select * from tb;
BHP|2020-01-03|3.25|1000.0|3250.0|
BHP|2020-01-04|3.5|2000.0|7000.0|
BHP|2020-01-05|3.55|1000.0|3550.0|
CSR|2020-01-03|5.55|1500.0|8325.0|
CSR|2020-01-04|5.6|2000.0|11200.0|
CSR|2020-01-05|5.55|2000.0|11100.0|
DDG|2020-01-03|10.2|4000.0|40800.0|
DDG|2020-01-04|10.25|4500.0|46125.0|
DDG|2020-01-05|10.3|5000.0|51500.0|
So far, so good. I can select a rolling average -
select code, date, avg(dv)
over (partition by code order by date asc rows 1 preceding)
from tb;
BHP|2020-01-03|3250.0
BHP|2020-01-04|5125.0
BHP|2020-01-05|5275.0
CSR|2020-01-03|8325.0
CSR|2020-01-04|9762.5
CSR|2020-01-05|11150.0
DDG|2020-01-03|40800.0
DDG|2020-01-04|43462.5
DDG|2020-01-05|48812.5
but when I try to put that selection into the avg_vo column of the table I don't get what I was expecting -
update tb set avg_dv =
(select avg(dv)
over (partition by code order by date asc rows 1 preceding)
);
select * from tb;
BHP|2020-01-03|3.25|1000.0|3250.0|3250.0
BHP|2020-01-04|3.5|2000.0|7000.0|7000.0
BHP|2020-01-05|3.55|1000.0|3550.0|3550.0
CSR|2020-01-03|5.55|1500.0|8325.0|8325.0
CSR|2020-01-04|5.6|2000.0|11200.0|11200.0
CSR|2020-01-05|5.55|2000.0|11100.0|11100.0
DDG|2020-01-03|10.2|4000.0|40800.0|40800.0
DDG|2020-01-04|10.25|4500.0|46125.0|46125.0
DDG|2020-01-05|10.3|5000.0|51500.0|51500.0
The avg_dv column has been updated with just the dv values, not the rolling average.
I also tried to adapt the answer here as
update tb
set tb.avg_dv = b.avg_dv
from tb as a inner join
(select code, date, avg(dv)
over (partition by code order by date asc rows 1 preceding)) b
on a.code = b.code and a.date = b.date;
But that just gives me syntax error -
Error: near ".": syntax error
In particular, the reference to "b" in the line
set tb.avg_dv = b.avg_dv
looks like garbage, but I don't know what to replace it with.
Can this be done in a single query?
The code that you tried to adapt in your case uses SQL Server syntax.
If your version of SQLite is 3.33.0+ the correct syntax is:
update tb
set avg_dv = b.avg_dv
from (
select code, date,
avg(dv) over (partition by code order by date asc rows 1 preceding) avg_dv
from tb
) b
where tb.code = b.code and tb.date = b.date;
If you are using a previous version of SQLite that does not support the FROM clause in the UPDATE statement, then you can do it with a CTE:
with cte as (
select code, date,
avg(dv) over (partition by code order by date asc rows 1 preceding) avg_dv
from tb
)
update tb
set avg_dv = (select c.avg_dv from cte c where c.code = tb.code and c.date = tb.date)
Also note that the update statement for the column dv can be written simply:
update tb set dv = cl * vo;
and if your version of SQLite is 3.31.0+, you could create it as generated column so that there is no need for updates:
alter table tb add column dv float generated always as(cl * vo);

Update multiple rows from select statement

I am trying to assign 'A' to [Student Details].group based on this SELECT statement.
SELECT TOP (10) PERCENT [Person Id], [Given Names], Surname, Gpa, [Location Cd]
FROM [Student Details]
WHERE ([Location Cd] = 'PAR')
ORDER BY Gpa DESC
I can't figure out how to use a SELECT statement in an UPDATE statement.
Can someone please explain how to accomplish this?
I am using ASP .NET and MsSQL Server if it makes a difference.
Thanks
I'm assuming you want to update these records and then return them :
SELECT TOP (10) PERCENT [Person Id], [Given Names], Surname, Gpa, [Location Cd]
INTO #temp
FROM [Student Details]
WHERE ([Location Cd] = 'PAR')
ORDER BY Gpa DESC
update [Student Details] set group='A' where [person id] in(select [person id] from #temp)
select * from #temp
I'm also assuming person id is the PK of student details
Try this using CTE (Common Table Expression):
;WITH CTE AS
(
SELECT TOP 10 PERCENT [Group]
FROM [Student Details]
WHERE ([Location Cd] = 'PAR')
ORDER BY Gpa DESC
)
UPDATE CTE SET [Group] = 'A'
Is this you want?
Update top (10) Percent [Student Details] set [group] = 'A'
where [Location Cd] = 'PAR' AND [group] is null

Qualify clause in Oracle

I'm working in Teradata to Oracle migration project. How can i modify the below query which is using QUALIFY in Teradata.
//QUERY 1
SELECT S.ID as Id,
S.MP_CD as Code,
S.GM_CD as GmCode,
S.GM_MSR_NBR as Mea_Year,
S.STTS_CD as YearCode,
S.TRMNTN_DTM as TerminationDate
FROM PD.RVY S, LOAD_LOG TLL
WHERE S.UPDTD_LOAD = TLL.LOG_KEY AND TLL.BLSH_CD = 'Y' AND S.STTS_CD IN ( 'C', 'P' )
QUALIFY ROW_NUMBER () OVER (PARTITION BY S.GM_CD ,S.MP_CD ,S.GM_MSR_NBR,S.STTS_CD
ORDER BY S.SO_DTM DESC
) = 1;
//Query 2
SELECT SP.ID,
SP.SO_DTM,
SP.TAX_ID,
SP.USER_ID,
SP.FRST_NM,
SP.LAST_NM,
SP.PHONE_NBR,
QSRP.TAX_ID,
QSRP.ROW_ID,
MAX(SP.SO_DTM) OVER (PARTITION BY SP.ID, SP.TAX_ID) MAX_SO_DTM
FROM VOPR_RMSY SP,VOPR_RMSY_SPNS QSRP
WHERE SP.ID =:URVYID AND QSRP.TAX_ID =:RPAXID
AND SP.ID = QSRP.ID AND SP.TAX_ID = QSRP.TAX_ID AND SP.SO_DTM = QSRP.SO_DTM
QUALIFY (SP.SO_DTM=MAX_SO_DTM AND QSRP.SO_DTM = MAX_SO_DTM)
GROUP BY SP.ID,SP.SO_DTM,SP.TAX_ID,SP.USER_ID,SP.FRST_NM,SP.LAST_NM,SP.PHONE_NBR,
QSRP.TAX_ID,QSRP.ROW_ID;
For this tried with HAVING instead of qualify but got an Error:
ORA-00904: "MAX_SO_DTM": invalid identifier
00904. 00000 - "%s: invalid identifier"
Seems like alias used for MAX is not working here....
Any of your help is really appreciated!
EDITED:
SELECT * FROM
(
SP.ID,
SP.SO_DTM,
SP.TAX_ID,
SP.USER_ID,
SP.FRST_NM,
SP.LAST_NM,
SP.PHONE_NBR,
QSRP.TAX_ID,
QSRP.ROW_ID,
MAX(SP.SO_DTM) OVER (PARTITION BY SP.ID, SP.TAX_ID) AS MAX_SO_DTM
FROM VOPR_RMSY SP,VOPR_RMSY_SPNS QSRP
WHERE SP.ID =:URVYID AND QSRP.TAX_ID =:RPAXID AND SP.ID = QSRP.ID AND SP.TAX_ID =
QSRP.TAX_ID AND SP.SO_DTM = QSRP.SO_DTM
GROUP BY SP.ID,SP.SO_DTM,SP.TAX_ID,SP.USER_ID,SP.FRST_NM,SP.LAST_NM,SP.PHONE_NBR,
QSRP.TAX_ID,QSRP.ROW_ID;
)dt WHERE (SP.SO_DTM=MAX_SO_DTM AND QSRP.SO_DTM = MAX_SO_DTM)
i know that i have to use alias dt for outer WHERE instead of SP and QSRP but here MAX_SO_DTM is compared with SO_DTM from two different tables. is there any other way to modify this?
Thanks!
Both QUALIFY and reusing an alias is Teradata specific.
Your try with HAVING is failing, because this is the logical sequence of processing a query:
FROM
WHERE
GROUP BY
HAVING
OLAP-function
QUALIFY -- Teradata specific
SAMPLE or EXPAND ON -- both are Teradata specific
ORDER
To resolve this you have to use a Derived Table/Inline View and move the QUALIFY condition into the outer WHERE.
SELECT *
FROM
(
SELECT S.ID as Id,
S.MP_CD as Code,
S.GM_CD as GmCode,
S.GM_MSR_NBR as Mea_Year,
S.STTS_CD as YearCode,
S.TRMNTN_DTM as TerminationDate,
ROW_NUMBER () OVER (PARTITION BY S.GM_CD ,S.MP_CD ,S.GM_MSR_NBR,S.STTS_CD
ORDER BY S.SO_DTM DESC) AS rn
FROM PD.RVY S, LOAD_LOG TLL
WHERE S.UPDTD_LOAD = TLL.LOG_KEY AND TLL.BLSH_CD = 'Y' AND S.STTS_CD IN ( 'C', 'P' )
) dt
WHERE rn = 1;
The same technique must be used when you want to reuse an alias.
EDIT:
The 2nd query can be rewritten as:
SELECT *
FROM
(
SELECT
SP.ID,
SP.SO_DTM,
SP.TAX_ID,
SP.USER_ID,
SP.FRST_NM,
SP.LAST_NM,
SP.PHONE_NBR,
QSRP.TAX_ID,
QSRP.ROW_ID,
MAX(SP.SO_DTM) OVER (PARTITION BY SP.ID, SP.TAX_ID) AS MAX_SO_DTM
FROM VOPR_RMSY SP,VOPR_RMSY_SPNS QSRP
WHERE SP.ID =:URVYID
AND QSRP.TAX_ID =:RPAXID
AND SP.ID = QSRP.ID
AND SP.TAX_ID = QSRP.TAX_ID
AND SP.SO_DTM = QSRP.SO_DTM
GROUP BY SP.ID,SP.SO_DTM,SP.TAX_ID,SP.USER_ID,SP.FRST_NM,SP.LAST_NM,SP.PHONE_NBR,
QSRP.TAX_ID,QSRP.ROW_ID
)dt
WHERE SO_DTM=MAX_SO_DTM
You don't need to do both comparisons *(SP.SO_DTM=MAX_SO_DTM AND QSRP.SO_DTM = MAX_SO_DTM)*, because they the 2nd is redundant as the tables are joined on *SP.SO_DTM = QSRP.SO_DTM*.
Otherwise you had to add QSRP.SO_DTM to the Derived Table using a different alias (e.g. QSRP_SO_DTM). In the outer level there's no more SP/QSRP but only dt, so it would be:
WHERE (SO_DTM=MAX_SO_DTM AND QSRP_SO_DTM = MAX_SO_DTM)

Preventing Max function from using timestamp as part of criteria on a date column in PL/SQL

If I query:
select max(date_created) date_created
on a datefield in PL/SQL (Oracle 11g), and there are records that were created on the same date but at different times, Max() returns only the latest times on that date. What I would like to do is have the times be ignored and return ALL records that match the max date, regardless of their associated timestamp in that column. What is the best practice for doing this?
Edit: what I'm looking to do is return all records for the most recent date that matches my criteria, regardless of varying timestamps for that day. Below is what I'm doing now and it only returns records from the latest date AND time on that date.
SELECT r."ID",
r."DATE_CREATED"
FROM schema.survey_response r
JOIN
(SELECT S.CUSTOMERID ,
MAX (S.DATE_CREATED) date_created
FROM schema.SURVEY_RESPONSE s
WHERE S.CATEGORY IN ('Yellow', 'Blue','Green')
GROUP BY CUSTOMERID
) recs
ON R.CUSTOMERID = recs.CUSTOMERID
AND R.DATE_CREATED = recs.date_created
WHERE R.CATEGORY IN ('Yellow', 'Blue','Green')
Final Edit: Got it working via the query below.
SELECT r."ID",
r."DATE_CREATED"
FROM schema.survey_response r
JOIN
(SELECT S.CUSTOMERID ,
MAX (trunc(S.DATE_CREATED)) date_created
FROM schema.SURVEY_RESPONSE s
WHERE S.CATEGORY IN ('Yellow', 'Blue','Green')
GROUP BY CUSTOMERID
) recs
ON R.CUSTOMERID = recs.CUSTOMERID
AND trunc(R.DATE_CREATED) = recs.date_created
WHERE R.CATEGORY IN ('Yellow', 'Blue','Green')
In Oracle, you can get the latest date ignoring the time
SELECT max( trunc( date_created ) ) date_created
FROM your_table
You can get all rows that have the latest date ignoring the time in a couple of ways. Using analytic functions (preferrable)
SELECT *
FROM (SELECT a.*,
rank() over (order by trunc(date_created) desc) rnk
FROM your_table a)
WHERE rnk = 1
or the more conventional but less efficient
SELECT *
FROM your_table
WHERE trunc(date_created) = (SELECT max( trunc(date_created) )
FROM your_table)

Resources