SELECT cust_detl.*,
MIN(CREATION_TIMESTAMP) OVER (PARTITION BY CUST_ID) AS MIN_TIMESTAMP
FROM CUST_DETAILS cust_detl
WHERE CREATION_TIMESTAMP=MIN_TIMESTAMP;
Above query select all columns from table CUST_DETAILS with oldest value inCREATION_TIMESTAMP column.
Any idea why MIN_TIMESTAMP encounter as an invalid identifier?
These are the columns that should display:
SELECT
CUSTOMER_DTL_SEQ.nextval,
CUST_ID
CUS_REF_ID
CUST_NAME
CUST_ADDRESS
CREATION_TIMESTAMP
FROM
(
CUSTOMER_DTL_SEQ.nextval,
cust_detl.CUST_ID,
cust_detl.CUST_REF_ID,
cust_detl.CUST_NAME,
cust_detl.CUST_ADDRESS,
cust_detl.CREATION_TIMESTAMP,
MIN(CREATION_TIMESTAMP) OVER (PARTITION BY CUST_ID) AS min_timestamp
FROM cust_details cust_detl
)
WHERE CREATION_TIMESTAMP = min_timestamp;
I would need to select CREATION_TIMESTAMP column as well, only those columns with minimum timestamp will be selected. The problem is the sequence with nextval is not allowed. I would need sequence in the query as this statment is going to use for INSERT later SELECT...INSERT INTO
The PK need to be incremented.
The column name is not yet valid, data is filtered first with the where condition and then on the filtered data, select statement works. You need to put it in sub query before you can use it.
SELECT * FROM
(SELECT cust_detl.*,
MIN(CREATION_TIMESTAMP) OVER (PARTITION BY CUST_ID) AS MIN_TIMESTAMP
FROM CUST_DETAILS cust_detl)
WHERE CREATION_TIMESTAMP=MIN_TIMESTAMP;
UPDATE: I don't know what list of columns you have in your table, but if you need only specific columns, then the query goes like this(assuming you need only columns cust_id, column1, column2 and column3 in select list)
SELECT cust_id,
column1,
column2,
column3
FROM (SELECT cust_detl.cust_id,
cust_detl.column1,
cust_detl.column2,
cust_detl.column3,
cust_detl.creation_timestamp,
MIN(creation_timestamp) over(PARTITION BY cust_id) AS min_timestamp
FROM cust_details cust_detl)
WHERE creation_timestamp = min_timestamp;
If you still don't get your solution, the post the list of columns from the table and the expected output.
Update2 : Fetch the cursor in the outer query, this query should work fine.
SELECT customer_dtl_seq.nextval,
cust_id,
cus_ref_id,
cust_name,
cust_address,
creation_timestamp
FROM (SELECT cust_detl.cust_id,
cust_detl.cust_ref_id,
cust_detl.cust_name,
cust_detl.cust_address,
cust_detl.creation_timestamp,
MIN(creation_timestamp) over(PARTITION BY cust_id) AS min_timestamp
FROM cust_details cust_detl)
WHERE creation_timestamp = min_timestamp;
Related
I have a requirement to do matching of few attributes one by one. I'm looking to avoid multiple select statements. Below is the example.
Table1
Col1|Price|Brand|size
-----------------------
A|10$|BRAND1|SIZE1
B|10$|BRAND1|SIZE1
C|30$|BRAND2|SIZE2
D|40$|BRAND2|SIZE4
Table2
Col1|Col2|Col3
--------------
B|XYZ|PQR
C|ZZZ|YYY
Table3
Col1|COL2|COL3|LIKECOL1|Price|brand|size
-----------------------------------------
B|XYZ|PQR|A|10$|BRAND1|SIZE1
C|ZZZ|YYY|D|NULL|BRAND2|NULL
In table3, I need to insert data from table2 by checking below conditions.
Find a match for record in table2, if Brand and size, Price match
If no match found, then try just Brand, Size
still no match found, try brand only
In the above example, for the first record in table2, found match with all the 3 attributes and so inserted into table3 and second record, record 'D' is matching but only 'Brand'.
All I can think of is writing 3 different insert statements like below into an oracle pl/sql block.
insert into table3
select from tab2
where all 3 attributes are matching;
insert into table3
select from tab2
where brand and price are matching
and not exists in table3 (not exists is to avoid
inserting the same record which was already
inserted with all 3 attributes matched);
insert into table3
select from tab2
where Brand is matching and not exists in table3;
Can anyone please suggest a better way to achieve it in any better way avoiding multiple times selecting from table2.
This is a case for OUTER APPLY.
OUTER APPLY is a type of lateral join that allows you join on dynamic views that refer to tables appearing earlier in your FROM clause. With that ability, you can define a dynamic view that finds all the matches, sorts them by the pecking order you've specified, and then use FETCH FIRST 1 ROW ONLY to only include the 1st one in the results.
Using OUTER APPLY means that if there is no match, you will still get the table B record -- just with all the match columns null. If you don't want that, you can change OUTER APPLY to CROSS APPLY.
Here is a working example (with step by step comments), shamelessly stealing the table creation scripts from Michael Piankov's answer:
create table Table1 (Col1,Price,Brand,size1)
as select 'A','10','BRAND1','SIZE1' from dual union all
select 'B','10','BRAND1','SIZE1' from dual union all
select 'C','30','BRAND2','SIZE2' from dual union all
select 'D','40','BRAND2','SIZE4'from dual
create table Table2(Col1,Col2,Col3)
as select 'B','XYZ','PQR' from dual union all
select'C','ZZZ','YYY' from dual;
-- INSERT INTO table3
SELECT t2.col1, t2.col2, t2.col3,
t1.col1 likecol1,
decode(t1.price,t1_template.price,t1_template.price, null) price,
decode(t1.brand,t1_template.brand,t1_template.brand, null) brand,
decode(t1.size1,t1_template.size1,t1_template.size1, null) size1
FROM
-- Start with table2
table2 t2
-- Get the row from table1 matching on col1... this is our search template
inner join table1 t1_template on
t1_template.col1 = t2.col1
-- Get the best match from table1 for our search
-- template, excluding the search template itself
outer apply (
SELECT * FROM table1 t1
WHERE 1=1
-- Exclude search template itself
and t1.col1 != t2.col1
-- All matches include BRAND
and t1.brand = t1_template.brand
-- order by match strength based on price and size
order by case when t1.price = t1_template.price and t1.size1 = t1_template.size1 THEN 1
when t1.size1 = t1_template.size1 THEN 2
else 3 END
-- Only get the best match for each row in T2
FETCH FIRST 1 ROW ONLY) t1;
Unfortunately is not clear what do you mean when say match. What is you expectation if there is more then one match?
Should it be only first matching or it will generate all available pairs?
Regarding you question how to avoid multiple inserts there is more then one way:
You could use multitable insert with INSERT first and condition.
You could join table1 to self and get all pairs and filter results in where condition
You could use analytical function
I suppose there is another ways. But why you would like to avoid 3 simple inserts. Its easy to read and maintain. And may be
There is example with analytical function next:
create table Table1 (Col1,Price,Brand,size1)
as select 'A','10','BRAND1','SIZE1' from dual union all
select 'B','10','BRAND1','SIZE1' from dual union all
select 'C','30','BRAND2','SIZE2' from dual union all
select 'D','40','BRAND2','SIZE4'from dual
create table Table2(Col1,Col2,Col3)
as select 'B','XYZ','PQR' from dual union all
select'C','ZZZ','YYY' from dual
with s as (
select Col1,Price,Brand,size1,
count(*) over(partition by Price,Brand,size1 ) as match3,
count(*) over(partition by Price,Brand ) as match2,
count(*) over(partition by Brand ) as match1,
lead(Col1) over(partition by Price,Brand,size1 order by Col1) as like3,
lead(Col1) over(partition by Price,Brand order by Col1) as like2,
lead(Col1) over(partition by Brand order by Col1) as like1,
lag(Col1) over(partition by Price,Brand,size1 order by Col1) as like_desc3,
lag(Col1) over(partition by Price,Brand order by Col1) as like_desc2,
lag(Col1) over(partition by Brand order by Col1) as like_desc1
from Table1 t )
select t.Col1,t.Col2,t.Col3, coalesce(s.like3, like_desc3, s.like1, like_desc1, s.like1, like_desc1),
case when match3 > 1 then size1 end as size1,
case when match1 > 1 then Brand end as Brand,
case when match2 > 1 then Price end as Price
from table2 t
left join s on s.Col1 = t.Col1
COL1 COL2 COL3 LIKE_COL SIZE1 BRAND PRICE
B XYZ PQR A SIZE1 BRAND1 10
C ZZZ YYY D - BRAND2 -
I have a table x_visa. I want to delete the duplicate columns from this table.
The query I am using for this is :
select * from (SELECT x_visa.*,
ROW_NUMBER() over (partition by effective_start_date, effective_end_date, person_id,
business_group_id, legislation_code , current_visa_permit, visa_permit_type, visa_permit_id, configuration_id
order by person_id) AS rn
from x_visa) T
WHERE rn > 1 );
The delete statement is giving an error :ORA-01752: cannot delete from view without exactly one key-preserved table
delete from
(select * from (SELECT x_visa.*,
ROW_NUMBER() over (partition by effective_start_date, effective_end_date, person_id,
business_group_id, legislation_code , current_visa_permit, visa_permit_type, visa_permit_id, configuration_id
order by person_id) AS rn
from x_visa) T
WHERE rn = 1 );
Is there a workaround to delete the duplicate data from this table ?
Each row has rowid identifier. So you can delete where rowid in results of your query.
delete from x_visa where rowid in (/*YOUR QUERY*/);
So we have:
delete from x_visa where rowid in (select r from (SELECT x_visa.rowid r, x_visa.*,
ROW_NUMBER() over (partition by effective_start_date, effective_end_date, person_id,
business_group_id, legislation_code , current_visa_permit, visa_permit_type, visa_permit_id, configuration_id
order by person_id) AS rn
from x_visa) T
WHERE rn > 1 ))
Apologies in advance, I am occasional Oracle user. I have put together a lookup table used by various functions/procedures and need to keep refresh this once a day with rows that either need removing or inserting. I have put together the following simply queries that return the columns against which I can determine the required action. Once I have returned my deletion data, I then need to delete from table A all records where the site_id and zone_ids match. I cant figure out the best way to achieve this, I have thought about running the select statements as cursors, but am not sure how I then delete the rows from table A using the site_id and zone_id from each record returned.
Query That returns records to be deleted from Table_A
SELECT site_id,zone_id,upper(ebts_switch_name)
FROM Table_A
minus
(SELECT site_id,zone_id, upper(ebts_switch_name)
FROM Table_B#remote_db
UNION
SELECT site_id,zone_id,upper(ebts_switch_name)
FROM Table_C);
Query That returns records to be Inserted into Table_A
SELECT cluster_id, site_id,zone_id, upper(trigram),upper(ebts_switch_name)
FROM Table_B#remote_db
WHERE site_id is NOT NULL
minus
SELECT cluster_name,site_id,zone_id,upper(trigram),upper(ebts_switch_name)
FROM Table_A
You can use your statements directly in the manner shown below:
DELETE FROM TABLE_A
WHERE (SITE_ID, ZONE_ID, UPPER(EBTS_SWITCH_NAME)) IN
(SELECT site_id, zone_id, upper(ebts_switch_name)
FROM Table_A
minus
(SELECT site_id, zone_id, upper(ebts_switch_name)
FROM Table_B#remote_db
UNION
SELECT site_id, zone_id, upper(ebts_switch_name)
FROM Table_C));
INSERT INTO TABLE_A (CLUSTER_NAME, SITE_ID, ZONE_ID, TRIGRAM, EBTS_SWITCH_NAME)
SELECT cluster_id, site_id, zone_id, upper(trigram), upper(ebts_switch_name)
FROM Table_B#remote_db
WHERE site_id is NOT NULL
minus
SELECT cluster_name, site_id, zone_id, upper(trigram), upper(ebts_switch_name)
FROM Table_A;
Best of luck.
I can't understand what do you mean by first query, cause it's almost same as
SELECT *
FROM table_a
MINUS
SELECT *
FROM table_a
means empty record set.
But generally, use DELETE syntax
DELETE
FROM table_a
WHERE (col1, col2) IN (SELECT col1, col2
FROM table_b);
And INSERT syntax
INSERT INTO table_a (col1, col2)
SELECT col1, col2
FROM table_b;
I have two tables
TABLE_A with columns project_id, id and load_date
and TABLE_B with columns project_id, delete_flag and delete_date
where TABLE_A.load_date is a new column and I want to populate it based on TABLE_B.delete_date for historic data. Basically, a file has been repeatedly loaded into the system and historically we didn't keep track of when it was loaded. However, each time the file is re-loaded, the previous version of it is updated in TABLE_B with a delete_date (i.e. a soft delete). The previous version just stays in TABLE_A without any changes.
I would like to populate TABLE_A.load_date based on matching projects in TABLE_B. The oldest row in TABLE_A (smallest TABLE_A.id) matches the oldest row in TABLE_B (oldest delete_date), etc. So the rows should match up if you keep picking the next one in order from each table. But I don't know how to turn that into an Oracle statement. What I've got so far is this which doesn't deal with matching on row order:
MERGE INTO TABLE_A a
USING
(
SELECT PROJECT_ID, DELETE_DATE
FROM TABLE_B
WHERE DELETE_FLAG = 'Y'
ORDER BY DELETE_DATE ASC
) b ON (a.PROJECT_ID = b.PROJECT_ID)
WHEN MATCHED THEN UPDATE
SET a.LOAD_DATE = p.DELETE_DATE;
This merge should do the work, as far as I properly understood your criteria:
merge into table_a ta
using (
select pid project_id, id, delete_date
from (
select project_id pid, id,
row_number() over (partition by project_id order by id) rn
from table_a) a
join (
select project_id pid, delete_date,
row_number() over (partition by project_id order by delete_date ) rn
from table_b
where delete_flag='Y') b using (pid, rn) ) tb
on (ta.project_id = tb.project_id and ta.id = tb.id)
when matched then update
set ta.load_date = tb.delete_date
I am creating a sub-query to select distinct entries on a certain column, DIS_COL, then return all other columns for those distinct entries, arbitrarily selecting the first row.
To do this I'm creating a sub-query that selects only first rows using over - partition by, then selecting from that sub-query.
There is an error with my code however; "ORA-00923: FROM keyword not found where expected".
My code is below:
select *
from (
select *,
row_number() over (partition by DIS_COL order by COL_2) as row_number --ORDER BY FIELD DETERMINES WHICH ROW IS THE FIRST ROW AND THUS WHICH ONE IS SELECTED.
from MY_TABLE
) as rows
where row_number = 1
AND CRITERIA_COL = 'CRIT_1'
OR CRITERIA_COL_2 = 'CRIT_2';
How can I correct my code to achieve the desired result?
I am working on an Oracle database.
Remove as rows. It is not proper syntax for the table/query alias. It is syntax for column alias.
select *
from (
select T.*,
row_number() over (partition by DIS_COL order by COL_2) as row_number --ORDER BY FIELD DETERMINES WHICH ROW IS THE FIRST ROW AND THUS WHICH ONE IS SELECTED.
from MY_TABLE t
)
where row_number = 1
AND (CRITERIA_COL = 'CRIT_1'
OR CRITERIA_COL_2 = 'CRIT_2');
It's not the ROW_NUMBER, it's the *, Add an alias to the subquery:
select *
from (
select T.*, -- here
row_number() over (partition by DIS_COL order by COL_2) as row_number --ORDER BY FIELD DETERMINES WHICH ROW IS THE FIRST ROW AND THUS WHICH ONE IS SELECTED.
from MY_TABLE
)T as rows -- and here
where row_number = 1
AND CRITERIA_COL = 'CRIT_1'
OR CRITERIA_COL_2 = 'CRIT_2';