Insert into Table with the first column being a Sequence - plsql

I am trying to use an Insert, Sequence and Select * to work together.
INSERT INTO BRK_INDV
Select * from (Select brk_seq.NEXTVAL as INDV_SEQ, a.*
FROM (select to_date(to_char(REQUEST_DATETIME,'DD-MM-YYYY'),'DD-MM-YYYY') BUSINESS_DAY, to_char(REQUEST_DATETIME,'hh24') src_hour,
CASE tran_type
WHEN 'V' THEN 'Visa'
WHEN 'M' THEN 'MasterCard'
ELSE tran_type
end text,
tran_type, count(*) as count
from DLY_STATS
where 1=1
AND to_date(to_char(REQUEST_DATETIME,'DD-MM-YYYY'),'DD-MM-YYYY') = '09-FEB-2015'
group by to_date(to_char(REQUEST_DATETIME,'DD-MM-YYYY'),'DD-MM-YYYY'),to_char(REQUEST_DATETIME,'hh24'),tran_type order by src_hour)a);
This gives me the following error:
ERROR at line 2:
ORA-02287: sequence number not allowed here
I tried to remove the order by and still the same error.
However, if I only run
Select brk_seq.NEXTVAL as INDV_SEQ, a.*
FROM (select to_date(to_char(REQUEST_DATETIME,'DD-MM-YYYY'),'DD-MM-YYYY') BUSINESS_DAY, to_char(REQUEST_DATETIME,'hh24') src_hour,
CASE tran_type
WHEN 'V' THEN 'Visa'
WHEN 'M' THEN 'MasterCard'
ELSE tran_type
end text,
tran_type, count(*) as count
from DLY_STATS
where 1=1
AND to_date(to_char(REQUEST_DATETIME,'DD-MM-YYYY'),'DD-MM-YYYY') = '09-FEB-2015'
group by to_date(to_char(REQUEST_DATETIME,'DD-MM-YYYY'),'DD-MM-YYYY'),to_char(REQUEST_DATETIME,'hh24'),tran_type order by src_hour)a;
It shows me proper entries. Then, why is select * not working for that?
Kindly help.

I see what you're trying to do. You want to insert rows into the BRK_INDV table in a particular order. The sequence number, which I assume will be the primary key of BRK_INDV, will be generated sequentially in the sorted order of the input rows.
You are working with a relational database. One of the first characteristics we all learn about a relational database is that the order of the rows in a table is insignificant. That's just a fancy word for fugitaboutit.
You cannot assume that a select * from table will return the rows in the same order they were written. It might. It might for quite a long time. Then something -- the number of rows, the grouping of some column values, the phase of the moon -- something will change and you will get them out in a seemingly totally random order.
If you want order, it must be imposed in the query, not the insert.
Here's the statement you should be executing:
INSERT INTO BRK_INDV
With
Grouped( Business_Day, Src_Hour, Text, Tran_Type, Count )As(
Select Trunc( Request_Datetime ) Business_Day,
To_Char( Request_Datetime, 'hh24') Src_Hour,
Case Tran_Type
When 'V' Then 'Visa'
When 'M' Then 'MasterCard'
Else Tran_Type
end Text,
Tran_Type, count(*) as count
from DLY_STATS
Where 1=1 --> Generated as dynamic SQL?
And Request_Datetime >= Date '2015-02-09'
And Request_Datetime < Date '2015-02-10'
Group By Trunc( Request_Datetime ), To_Char( Request_Datetime, 'hh24'), Tran_Type
)
Select brk_seq.Nextval Indv_Seq, G.*
from Grouped G;
Notice there is no order by. If you want to see the generated rows in a particular order:
select * from Brk_Indv order by src_hour;
Since there could be hundreds or thousands of transactions in any particular hour, you probably order by something other than hour anyway.
In Oracle, the trunc function is the best way to get a date with the time portion stripped away. However, you don't want to use it in the where clause (or, aamof, any other function such as to_date or to_char)as that would make the clause non-sargable and result in a complete table scan.

The problem is that you can't use a sequence in a subquery. For example, this gives the same ORA-02287 error you are getting:
create table T (x number);
create sequence s;
insert into T (select * from (select s.nextval from dual));
What you can do, though, is create a function that returns nextval from the sequence, and use that in a subquery:
create function f return number as
begin
return s.nextval;
end;
/
insert into T (select * from (select f() from dual));

Related

Get LIMIT value from subquery result

I would like to use the LIMIT option in my query, but the number of expected rows is stored in another table. This is what I have, but it doesn't work:
select * from table1 limit (select limitvalue from table2 where id = 1)
When I only run the subquery, the result is 6, as expected.
I prefer working with a WITH statement if possible, but that didn't work eiter.
Thank you in advance!
You could use a prepared statement to get the limit of queries from the other table because the limit clause does not allow non constant variables as parameter:
PREPARE firstQuery FROM "SELECT * FROM table1 LIMIT ?";
SET #limit = (select limitvalue from table2 where id = 1);
EXECUTE firstQuery USING #limit;
The source of the sql query from another post
You can make use of MariaDB's ROW_NUMBER function in a CTE to count the rows to be output, comparing that against the limitvalue. For example:
WITH rownums AS (
SELECT *,
ROW_NUMBER() OVER () AS rn
FROM table1
)
SELECT *
FROM rownums
WHERE rn <= (SELECT limitvalue FROM table2 WHERE id = 1)
Note Using LIMIT without ORDER BY is not guaranteed to give you the same results every time. You should include an ORDER BY clause in the OVER part of the ROW_NUMBER window function. With the sample data in my demo, you might use something like:
ROW_NUMBER() OVER (ORDER BY mark DESC)
Demo on dbfiddle

Problem inserting database row using last row in sqlite

I'd like to be able to do the following initially and also at anytime.
insert into balance (closing_amount, opening_amount, created, tx_id)
select closing_amount + :value, closing_amount, :date, :tx_id from balance order by id desc limit 1
Basically I'm inserting by using previous values. But if there are no values to begin with, nothing gets inserted.
I could use a union to which works the first time but duplicates on subsequent inserts.
I want to avoid two trips. Is there a way to do this?
Also, the tx_id will always be unique.
I think you want something like this:
insert into balance (closing_amount, opening_amount, created, tx_id)
select coalesce(max(closing_amount), 0) + :value,
coalesce(max(closing_amount), 0),
:date,
:tx_id
from (
select closing_amount
from balance
order by tx_id desc
limit 1
) t;
You only need the last closing_amount, so max(closing_amount) from the subquery, which returns 1 row or none at all, will return that closing_amount or null respectively.
See a simplified demo.

Subquery returns more than one in row in PL/SQL stored procedure

I am a complete novice to PL/SQL and this is an attempt to start writing stored procedures . The case is i want to simply find out inside my stored procedure what is the name of the employee who is getting the highest salary for a particular department (which is passed as a parameter to the stored procedure).
Below is a screen shot of my tables:
The code of my stored procedure is as follows :
create or replace procedure High_salary(Dept_Name IN varchar2)
/*RETURN varchar2 */
AS
EMP_NAME_var varchar2(100) := '';
Begin
dbms_output.put_line('****'||Dept_Name);
select EMP_NAME INTO EMP_NAME_var
from(
select EMP_NAME,rank() over(order by salary desc) rn from employee
where DEPT_CD=(select DEPT_CD from DEPARTMENT where DEPT_NAME=Dept_Name)) a where rn=1;
/*RETURN EMP_NAME_var;*/
END;
when i run this i get this error:
Connecting to the database LOCAL_DEV_DB.
ORA-01427: single-row subquery returns more than one row
ORA-06512: at "LOCAL_DEV_DB.HIGH_SALARY", line 7
ORA-06512: at line 6
****'Technology'
Process exited.
Disconnecting from the database LOCAL_DEV_DB.
However when i run the subquery separate it gets only one row as expected :
select EMP_NAME
from(
select EMP_NAME,rank() over(order by salary desc) rn from employee
where DEPT_CD=(select DEPT_CD from DEPARTMENT where DEPT_NAME='Technology')) a where rn=1;
Can someone please point out what i am missing here.
The problem most likely comes from the fact that your variable name is the same as the field name. In the procedure, it is comparing Dept_Name with itself, and unsurprisingly, all rows match.
In the procedure, try naming the variable DeptNameVar or similar, update the reference in the query, and see if that helps.
I would express your logic as a join between the two tables. Then, use ROW_NUMBER to identify the record for a given department corresponding to the employee with the highest salary.
create or replace procedure High_salary (Dept_Name IN varchar2)
AS
EMP_NAME_var varchar2(100) := '';
Begin dbms_output.put_line('****'||Dept_Name);
select EMP_NAME INTO EMP_NAME_var
from
(
select e.EMP_NAME, ROW_NUMBER() OVER (ORDER BY e.SALARY DESC) rn
FROM employee e
INNER JOIN department d
ON e.DEPT_CD = d.DEPT_CD
WHERE d.DEPT_NAME = Dept_Name
) t
where rn = 1
END;
The problem with your current approach is not necessarily the WHERE clause, which should be working, but rather than you are using the RANK function. RANK would return 1 should two or more employees be tied for the highest salary.
By using ROW_NUMBER, you ensure that the subquery would only ever return a single row.
You are getting that error because of this part of the statement
select EMP_NAME,rank() over(order by salary desc) rn from employee.
You are pulling two columns one is EMP_NAME and other is rank() over(order by salary desc) rn.That's why you are getting that error.
Please modify the subquery such that you only select emp_name.

id invalid column in cursor though my table has column id

My table has a defined column id.
Though the same query runs fin e independently,but when I try to declare it in a cursor, it shows invalid column.
declare
type PA_1 is record (PI number);
calc number;
row_container PA_1;
begin
for row_container in
(
select distict t1.pi , t2.id
from table1 t1, table2 t2
where t1.Pi=t2.pi
);
Loop
select calculation to calc
from table1 t1
where t1.pi=row_container.pi and t2.id=row_container.id;
end loop;
commit;
end;
the inner query runs fine otherwise. Please help
Several syntax errors:
First of all, you should remove the declaration for row_container. for row_container in () makes this inplicitely (and your declaration with one column doesn't even match your query with two columns).
distict should be distinct.
Then remove the semicolon before the Loop keyword. It doesn't belong there.
Then select calculation to calc should be select calculation into calc.
Inside the loop you select from table1 (which you call t1 again), but your where clause contains t2.id, while there is no t2 in that query.
And then: What is this routine supposed to do? It selects some value into the variable calc, but doesn't use it. So once it runs, it just doesn't do anything.

Is it possible to use WHERE clause in same query as PARTITION BY?

I need to write SQL that keeps only the minimum 5 records per each identifiable record in a table. For this, I use partition by and delete all records where the value returned is greater than 5. When I attempt to use the WHERE clause in the same query as the partition by statement, I get the error "Ordered Analytical Functions not allowed in WHERE Clause". So, in order to get it to work, I have to use three subqueries. My SQL looks ilke this:
delete mydb.mytable where (field1,field2) in
(
select field1,field2 from
(
select field1,field2,
Rank() over
(
partition BY field1
order by field1,field2
) n
from mydb.mytable
) x
where n > 5
)
The innermost subquery just returns the raw data. Since I can't use WHERE there, I wrapped it with a subquery, the purpose of which is to 1) use WHERE to get records greater than 5 in rank and 2) select only field1 and field2. The reason why I select only those two fields is so that I can use the IN statement for deleting those records in the outermost query.
It works, but it appears a bit cumbersome. I'd like to consolidate the inner two subqueries into a single subquery. Is this possible?
Sounds like you need to use the QUALIFY clause which is the HAVING clause for Window Aggregate functions. Below is my take on what you are trying to accomplish.
Please do not run this SQL directly against your production data without first testing it.
/* Physical Delete */
DELETE TGT
FROM MyDB.MyTable TGT
INNER JOIN
(SELECT Field1
, Field2
FROM MyDB.MyTable
QUALIFY ROW_NUMBER() (PARTITION BY Field1, ORDER BY Field1,2)
> 5
) SRC
ON TGT.Field1 = SRC.Field1
AND TGT.Field2 = SRC.Fileld2
/* Logical Delete */
UPDATE TGT
FROM MyDB.MyTable TGT
,
(SELECT Field1
, Field2
FROM MyDB.MyTable
QUALIFY ROW_NUMBER() (PARTITION BY Field1, ORDER BY Field1,2)
> 5
) SRC
SET Deleted = 'Y'
/* RecordExpireDate = Date - 1 */
WHERE TGT.Field1 = SRC.Field1
AND TGT.Field2 = SRC.Fileld2

Resources