I have a simple hierarchy structure in a table (Using Oracle Database 11g)
A
|-B
|-B1
|-C
|-D
Each node has a property associated with it (Y or N).
Traversing from the root node (parent), I want to get the first node in each branch that has the property Y.
For example:
A
|-B (N)
|-B1 (Y)
|--B2 (N)
|-C (Y)
|-C1 (Y)
|--C2 (N)
|-D (Y)
This should return B1, C and D.
Please give some ideas on how this can be done (code needs to be optimized for time).
There's probably a fancier way than using sys_connect_by_path, but I find it very easy to understand.
create table hier_test (id varchar2(2), parent_id varchar2(2), prop varchar2(1));
insert into hier_test values ('A', null, 'N');
insert into hier_test values ('B', 'A', 'N');
insert into hier_test values ('C', 'A', 'Y');
insert into hier_test values ('D', 'A', 'Y');
insert into hier_test values ('B1', 'B', 'Y');
insert into hier_test values ('B2', 'B1', 'N');
insert into hier_test values ('C1', 'C', 'Y');
insert into hier_test values ('C2', 'C1', 'N');
select id from (
select id, prop, sys_connect_by_path(prop, '>') as priors
from hier_test
connect by parent_id = prior id
start with id = 'A'
)
where prop = 'Y' and priors not like '%Y>%';
Edit: sqlfiddle
Related
In a table I have a repeating field that I want to group by but without messing up the table.
The data I have is the following:
and i want to get to this:
As you can see, group A is repeated and it's what I want. The problem is that it groups all the cities A into a single group and the table is messed up. Can you help me?
As per my comment you will need something to order the rows by then it's a rather clunky solution to get what you need. There will probably be a much more elegant way but this will work.
The script reproduces you data, adds a row id and adds a groupid column.
You can use the group ID column to group your data by in the report even if it's not visible.
declare #t TABLE(client varchar(10), product varchar(10), [value] int, city varchar(10))
INSERT INTO #t VALUES
('A', 'ASD', 13, 'A'),
('B', 'QWE', 40, 'A'),
('G', 'SDF', 31, 'A'),
('F', 'ERT', 12, 'B'),
('D', 'DFG', 2, 'B'),
('G', 'GFHJ', 4, 'C'),
('B', 'TY', 13, 'C'),
('V', 'URTY', 29, 'A'),
('C', 'ERT', 33, 'A')
DROP TABLE IF EXISTS #r
CREATE TABLE #r (client varchar(10), product varchar(10), [value] int, city varchar(10), RowID int IDENTITY(1,1), GroupID int)
-- this just adds a sort order for the data as #r has an identity column
INSERT INTO #r (client, product, [value], city)
SELECT client, product, [value], city FROM #t
-- some variables to track the previous city and group counter
DECLARE #g int = 0 -- counter for row group
DECLARE #prvCity varchar(10) ='' -- previous city
WHILE EXISTS (SELECT * FROM #r WHERE GroupID IS NULL) -- for any rows not updated
BEGIN -- put the rowid into #r and city into #curCity
declare #r int = (SELECT MIN(RowID) FROM #r WHERE GroupID is null)
declare #curCity varchar(10) = (SELECT city from #r WHERE RowID = #r)
IF #prvCity != #curCity -- if the city changed
BEGIN
SET #g = #g + 1 -- increment the group
SET #prvCity = #curCity -- set previous city to current city
END
UPDATE #r SET GroupID = #g WHERE RowID = #r -- update the temp table
END
-- final output dataset query
SELECT * FROM #r
Here is the report design including the row groups
and here is the final output
I have created the table successfully but not able to insert multiple values by this code. What is the mistake. Oracle Code
CREATE TABLE sales (
customer_id VARCHAR2(4)
,order_date DATE
,product_id VARCHAR2(5)
);
INSERT INTO sales
VALUES (
'A'
,'01-JAN-2021'
,'2'
);(
'A'
,'07-JAN-2021'
,'2'
);(
'A'
,'10-JAN-2021'
,'3'
);(
'A'
,'11-JAN-2021'
,'3'
);(
'A'
,'11-JAN-2021'
,'3'
);(
'B'
,'01-JAN-2021'
,'2'
);(
'B'
,'02-JAN-2021'
,'2'
);(
'B'
,'04-JAN-2021'
,'1'
);(
'B'
,'11-JAN-2021'
,'1'
);(
'B'
,'16-JAN-2021'
,'3'
);(
'B'
,'01-FEB-2021'
,'3'
);(
'C'
,'01-JAN-2021'
,'3'
);(
'C'
,'01-JAN-2021'
,'3'
);(
'C'
,'07-JAN-2021'
,'3'
);
Why? because that's invalid syntax in Oracle.
One option is this:
SQL> INSERT INTO sales VALUES ('A', DATE '2021-01-01', '2');
1 row created.
SQL> INSERT INTO sales VALUES ('A', DATE '2021-01-07', '2');
1 row created.
SQL> INSERT INTO sales VALUES ('A', DATE '2021-01-10', '3');
1 row created.
etc.
Note that you shouldn't insert strings into date datatype column; I used date literal; you could use TO_DATE with appropriate format model.
Another option is e.g.
SQL> INSERT ALL
2 INTO sales VALUES ('A', DATE '2021-01-01', '2')
3 INTO sales VALUES ('A', DATE '2021-01-07', '2')
4 SELECT * FROM DUAL;
2 rows created.
or
SQL> INSERT INTO sales
2 SELECT 'A', DATE '2021-01-01', '2' FROM DUAL
3 UNION ALL
4 SELECT 'A', DATE '2021-01-07', '2' FROM DUAL;
2 rows created.
I'm looking for a good SQL approach (Oracle database) to fulfill the next requirements:
Delete rows from Table A that are not present in Table B.
Both tables have identical structure
Some fields are nullable
Amount of columns and rows is huge (more 100k rows and 20-30 columns to compare)
Every single field of every single row needs to be compared from Table A against table B.
Such requirement is owing to a process that must run every day as changes will come from Table B.
In other words: Table A Minus Table B => Delete the records from the Table A
delete from Table A
where (field1, field2, field3) in
(select field1, field2, field3
from Table A
minus
select field1, field2, field3
from Table B);
It's very important to mention that a normal MINUS within DELETE clause fails as does not take the nulls on nullable fields into consideration (unknown result for oracle, then no match).
I also tried EXISTS with success, but I have to use NVL function to replace the nulls with dummy values, which I don't want it as I cannot guarantee that the value replaced in NVL will not come as a valid value in the field.
Does anybody know a way to accomplish such thing? Please remember performance and nullable fields as "a must".
Thanks ever
decode finds sameness (even if both values are null):
decode( field1, field2, 1, 0 ) = 1
To delete rows in table1 not found in table2:
delete table1 t
where t.rowid in (select t1.rowid
from table1 t1
left outer join table2 t2
on decode(t1.field1, t2.field1, 1, 0) = 1
and decode(t1.field2, t2.field2, 1, 0) = 1
and decode(t1.field3, t2.field3, 1, 0) = 1
/* ... */
where t2.rowid is null /* no matching row found */
)
to use existing indexes
...
left outer join table2 t2
on (t1.index_field1=t2.index_field1 or
t1.index_field1 is null and t2.index_field1 is null)
and ...
Use a left outer join and test for null in your where clause
delete a
from a
left outer join b on a.x = b.x
where b.x is null
Have you considered ORALCE SQL MERGE statement?
Use Bulk operation for huge number of records. Performance wise it will be faster.
And use join between two table to get rows to be delete. Nullable columns can be compared with some default value.
Also, if you want Table A to be similar as Table B, why don't you truncate table A and then insert data from table b
Assuming you the same PK field available on each table...(Having a PK or some other unique key is critical for this.)
create table table_a (id number, name varchar2(25), dob date);
insert into table_a values (1, 'bob', to_date('01-01-1978','MM-DD-YYYY'));
insert into table_a values (2, 'steve', null);
insert into table_a values (3, 'joe', to_date('05-22-1989','MM-DD-YYYY'));
insert into table_a values (4, null, null);
insert into table_a values (5, 'susan', to_date('08-08-2005','MM-DD-YYYY'));
insert into table_a values (6, 'juan', to_date('11-17-2001', 'MM-DD-YYYY'));
create table table_b (id number, name varchar2(25), dob date);
insert into table_b values (1, 'bob', to_date('01-01-1978','MM-DD-YYYY'));
insert into table_b values (2, 'steve',to_date('10-14-1992','MM-DD-YYYY'));
insert into table_b values (3, null, to_date('05-22-1989','MM-DD-YYYY'));
insert into table_b values (4, 'mary', to_date('12-08-2012','MM-DD-YYYY'));
insert into table_b values (5, null, null);
commit;
-- confirm minus is working
select id, name, dob
from table_a
minus
select id, name, dob
from table_b;
-- from the minus, re-query to just get the key, then delete by key
delete table_a where id in (
select id from (
select id, name, dob
from table_a
minus
select id, name, dob
from table_b)
);
commit;
select * from table_a;
But, if at some point in time, tableA is to be reset to the same as tableB, why not, as another answer suggested, truncate tableA and select all from tableB.
100K is not huge. I can do ~100K truncate and insert on my laptop instance in less than 1 second.
> DELETE FROM purchase WHERE clientcode NOT IN (
> SELECT clientcode FROM client );
This deletes the rows from the purchase table whose clientcode are not in the client table. The clientcode of purchase table references the clientcode of client table.
DELETE FROM TABLE1 WHERE FIELD1 NOT IN (SELECT CLIENT1 FROM TABLE2);
I'm facing one annoying problema and I would like some help.
This is the situation.
CREATE TABLE tree_hierarchy (
id NUMBER (20)
,parent_id NUMBER (20)
);
CREATE TABLE tree_information (
id NUMBER (20)
,some_text VARCHAR(20)
,tree_id NUMBER (20)
);
INSERT INTO tree_hierarchy (id, parent_id) VALUES (2, null);
INSERT INTO tree_hierarchy (id, parent_id) VALUES (4, 2);
INSERT INTO tree_hierarchy (id, parent_id) VALUES (9, 4);
INSERT INTO tree_hierarchy (id, parent_id) VALUES (20, null);
INSERT INTO tree_hierarchy (id, parent_id) VALUES (40, 20);
INSERT INTO tree_hierarchy (id, parent_id) VALUES (90, 40);
INSERT INTO tree_information (id, some_text, tree_id) VALUES (10,'Some teste', 2);
INSERT INTO tree_information (id, some_text, tree_id) VALUES (11,'Other tree', 20);
And i would like to do something like this.
SELECT hier.*
FROM tree_information Ti
JOIN (
SELECT
id,
parent_id
FROM tree_hierarchy th
where connect_by_isleaf = 1
START WITH th.id = ti.tree_id
CONNECT BY PRIOR th.id = th.parent_id
) hier on 1=1;
but ti.tree_id is not visible inside the select.
If I change the start with condition for
START WITH th.parent_id is null
Will stay wrong.
Someone has idea how to solve this situation ?
I would appreciate if you provide expected result explicitly.
My best guess is:
SELECT hier.*, ti.*
FROM tree_information Ti
JOIN (
SELECT
id,
parent_id,
connect_by_root th.id as tree_id
FROM tree_hierarchy th
where connect_by_isleaf = 1
START WITH th.id in ( select tii.tree_id from tree_information Tii)
CONNECT BY PRIOR th.id = th.parent_id
) hier on ti.tree_id = hier.tree_id;
ID PARENT_ID TREE_ID ID SOME_TEXT TREE_ID
9 4 2 10 Some teste 2
90 40 20 11 Other tree 20
I have a situation where a field can be NULL when another field is certain values and for others it should be NOT NULL.
"Type" VARCHAR2(30) NOT NULL,
BestandLocatie VARCHAR2(150) NULL,
I made two constraints, the first one makes sure that only certain values in "Type" can be entered.
CONSTRAINT TypeCheck
CHECK ("Type" IN ('Tab', 'Bass Tab', 'Chords', 'Power Tab', 'Guitar Pro',
'Video Lesson', 'Drum Tab', 'Ukulele Chords')),
The other constraint (which gives an error, missing right parenthesis) should make sure that BestandLocatie is NOT NULL when "Type" is certain types:
CONSTRAINT BestandLocatieCheck
CHECK (BestandLocatie IS NOT NULL WHERE ("Type" IN ('Power Tab', 'Guitar Pro'
'Video Lesson')))
When I searched for the Where clause I only found examples of it in select statements. Does this mean that I can't use it here, is there an other method of doing this, or do I have to check this in the end application or can it only be done in PLSQL?
You can do something like this:
alter table foo add (constraint check_b
check ( (a in ('a', 'b') and b is not null)
or (a not in ('a', 'b') /* and b is null */)
)
);
The commented and b is null should be there depending on whether you want to require the value to be null in the other cases or not.
Demo:
SQL> create table foo (a varchar(2) not null, b varchar(2));
SQL> alter table foo add (constraint check_b check (
(a in ('a', 'b') and b is not null) or (a not in ('a', 'b') and b is null))
);
Table altered.
SQL> insert into foo values ('a', 'b');
1 row created.
SQL> insert into foo values ('a', null);
insert into foo values ('a', null)
*
ERROR at line 1:
ORA-02290: check constraint (MAT.CHECK_B) violated
SQL> insert into foo values ('c', null);
1 row created.
SQL> insert into foo values ('c', 'b');
insert into foo values ('c', 'b')
*
ERROR at line 1:
ORA-02290: check constraint (MAT.CHECK_B) violated