Join Delete in Oracle 11g - oracle11g

I'm having a hard time converting my sybase script into an Oracle 11g script. I used the translation tool but I got even more confuse with the Rowid.
Is there any easier way to do it without using rowid so it's easier for me, who just learn Oracle, to digest?
Here is my Sybase script:
delete table_1
from table_1 a, table_2 b
where (select count(*) from table_2
where a.id = id
and a.seq = seq
and a.gcode = gcode
and a.gtype = gtype) = 0
and a.id = b.id
and a.seq = b.seq;
Here is the result from Oracle SQL development translation tool:
DELETE table_1
WHERE ROWID IN
( SELECT a.ROWID
FROM table_1 a,
table_2 b
WHERE ( SELECT COUNT(*)
FROM table_2
WHERE a.id = id
AND a.seq = seq
AND a.gcode = gcode
AND a.gtype = gtype ) = 0
AND a.id = b.id
AND a.seq = b.seq );
Is it safe to write using ID instead of RowId?:
IDs in both table_1 and table_2 refer to the same PK. Table_1 and table_2 are mirror of each other.
DELETE table_1
WHERE ID IN
( SELECT DISTINCT a.ID
FROM table_1 a,
table_2 b
WHERE ( SELECT COUNT(*)
FROM table_2
WHERE a.id = id
AND a.seq = seq
AND a.gcode = gcode
AND a.gtype = gtype ) = 0
AND a.id = b.id
AND a.seq = b.seq );

Rowid identifies a row. Two rows cannot have the same rowid. If the id in your table is the primary key (not part if a composite key) then you basically achieve the same by using id. However rowid is generally provides faster access as it references the physical location of a row and does not have to access the row by using the index.

beware of partitioning. There can be same rowid on different partitions, so the above statement about uniqnuness of rowid is not valid for partitioned tables

Related

How to select max() value of some of the selected rows?

There are two types of rows in document_files table - that have active=1 or active=0. Each document (document_id) can have multiple rows of each type. The goal is to for every document select all rows that have active=1, but only the latest one of all rows that have active=0.
select *
from documents d
inner join document_files df on df.document_id = d.id
where ...
-- df.active = 1, select them all
-- or df.active = 0, only select max(df.last_stamp) of all df.active=0 rows for this df.document_id
How to achieve this?
I'd use union and group by, but the problem is that a lot of columns are returned, and it wouldn't be optimal to group them all.
This section only get the document files with no active record and after that using max () gets the most recent record for the item:
select document_id
from (select * from document_files f where f.document_id not in
(select document_id from document_files where active = 1)) dff where dff.last_stamp =
(
select max(i.last_stamp) from document_files i where i.document_id = dff.document_id
)
My final sql :
select *
from documents d
inner join document_files df on df.document_id = d.id
where df.active = 1 or
df.document_id in
(
select document_id
from (select * from document_files f where f.document_id not in
(select document_id from document_files where active = 1)) dff where dff.last_stamp =
(
select max(i.last_stamp) from document_files i where i.document_id = dff.document_id
)
)
Please add some sql fiddle next time so we can test the result

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);

Teradata macro with volatile table and CTE to insert data into a table

I need to create a teradata macro to extract information into a volatile table first, then do CTE to extract data from this volatile table and insert into a teradata table, tried different ways all fail, appreciate help!
CREATE MACRO database.macro_insertion_tablename AS (
CREATE VOLATILE TABLE vt AS
(
SELECT
id, bu,
CONCAT(TO_CHAR(comment_date, 'yyyy-mm-dd HH24:MI:SS'), ' ', action) AS full_action,
ROW_NUMBER() OVER (PARTITION BY id ORDER BY date DESC) AS row_num,
COUNT(*) OVER (PARTITION BY id) as cnt
FROM database.table1
) WITH DATA UNIQUE PRIMARY INDEX(id, row_num) ON COMMIT PRESERVE ROWS;
WITH RECURSIVE cte (id, bu, act, rn) AS
(
SELECT
id, bu
,CAST(full_action AS VARCHAR(5000)) AS full_action
,row_num
FROM vt
WHERE row_num = cnt
UNION ALL
SELECT
vt.id, vt.bu
,cte.act || ' / ' || vt.full_action
,vt.row_num
FROM vt
JOIN cte On vt.id = cte.id AND vt.row_num = cte.rn - 1
)
INSERT INTO database.table (id, bu, full_action)
SELECT id, bu, act
FROM cte
WHERE rn = 1;
DROP TABLE vt;
);
DDL must be the only statement in a Teradata Macro.
As workaround you could switch to a Global Temporary Table which is defined once and then you simply Insert/Select into it instead of CREATE VOLATILE TABLE.
But in your case there's no need for a temp table plus inefficient recursive processing to get a "group concat":
SELECT id, max(bu) -- maybe min(bu)?
XmlAgg(Concat(To_Char(comment_date, 'yyyy-mm-dd HH24:MI:SS'), ' ', action)
ORDER BY comment_date) (VARCHAR(5000)) AS full_action
FROM database.table1
GROUP BY 1
will give you a similar result.
To follow up on my comments, you should be able to define multiple CTEs in the same statement. It may be tricky getting the RECURSIVE CTE to work, but it sounds like it's possible. Maybe something like this:
CREATE MACRO database.macro_insertion_tablename AS (
WITH vt (id, bu, full_action, row_num, cnt) AS
(
SELECT
id, bu,
CONCAT(TO_CHAR(comment_date, 'yyyy-mm-dd HH24:MI:SS'), ' ', action) AS full_action,
ROW_NUMBER() OVER (PARTITION BY id ORDER BY date DESC) AS row_num,
COUNT(*) OVER (PARTITION BY id) as cnt
FROM database.table1
),
RECURSIVE cte (id, bu, act, rn) AS
(
SELECT
id, bu
,CAST(full_action AS VARCHAR(5000)) AS full_action
,row_num
FROM vt
WHERE row_num = cnt
UNION ALL
SELECT
vt.id, vt.bu
,cte.act || ' / ' || vt.full_action
,vt.row_num
FROM vt
JOIN cte On vt.id = cte.id AND vt.row_num = cte.rn - 1
)
INSERT INTO database.table (id, bu, full_action)
SELECT id, bu, act
FROM cte
WHERE rn = 1;
);
I don't have a Teradata system to test with, so not 100% it will work as-is, but give it a try. You may need to change RECURSIVE to WITH RECURSIVE and also the ordering of the CTE queries (i.e. put the RECURSIVE one first). Take a look at these two links:
Teradata Forum - Multiple With Clause
teradata Forum - Common Table Expressions

SQLITE, Create a temp table then select from it

just wondering how i can create a temp table and then select from it further down the script.
Example.
CREATE TEMPORARY TABLE TEMP_TABLE1 AS
Select
L.ID,
SUM(L.cost)/2 as Costs,
from Table1 L
JOIN Table2 C on L.ID = C.ID
Where C.name = 'mike'
Group by L.ID
Select
Count(L.ID)
from Table1 L
JOIN TEMP_TABLE1 TT1 on L.ID = TT1.ID;
Where L.ID not in (TT1)
And Sum(L.Cost) > TT1.Costs
Ideally I want to have a temp table then use it later in the script to reference from.
Any help would be great!
You simply refer to the table as temp.<table> or <table> the latter only if it is a unique table name.
As per :-
If a schema-name is specified, it must be either "main", "temp", or
the name of an attached database. In this case the new table is
created in the named database. If the "TEMP" or "TEMPORARY" keyword
occurs between the "CREATE" and "TABLE" then the new table is created
in the temp database. It is an error to specify both a schema-name and
the TEMP or TEMPORARY keyword, unless the schema-name is "temp". If no
schema name is specified and the TEMP keyword is not present then the
table is created in the main database.
SQL As Understood By SQLite - CREATE TABLE
The following example creates 3 tables :-
table1 with 3 columns as a permanent table.
table1 a temporary copy of the permanent table1.
temp_table another temporary copy of the permanent table1.
:-
DROP TABLE IF EXISTS temp.table1;
DROP TABLE IF EXISTS table1;
DROP TABLE IF EXISTS temp_table;
CREATE TABLE table1 (columnA INTEGER,columnB INTEGER, columnC INTEGER);
When creating the permanent table 1 it is loaded with 4 rows
:-
INSERT INTO table1 (columnA,columnB,columnC) VALUES
(1,5,20),
(2,7,21),
(3,8,80),
(4,3,63);
CREATE TEMP TABLE table1 AS select * from table1;;
CREATE TEMPORARY TABLE temp_table AS SELECT * FROM table1;
both temp tables are then used to in a union all to basically duplicate the rows, but with an indicator of the source table as a new column from_table
Not that two forms of referring to the temp tables are used. temp. and just the table name.
The latter only usable if the temporary table is a unique table name.
:-
SELECT 'temp_table' AS from_table,* FROM temp_table
UNION ALL
SELECT 'temp.table1' as from_table,* FROM temp.table1;
The result being :-
Re addition of example :-
CREATE TEMPORARY TABLE TEMP_TABLE1 AS
Select
L.ID,
SUM(L.cost)/2 as Costs,
from Table1 L
JOIN Table2 C on L.ID = C.ID
Where C.name = 'mike'
Group by L.ID
Select
Count(L.ID)
from Table1 L
JOIN TEMP_TABLE1 TT1 on L.ID = TT1.ID;
Where L.ID not in (TT1)
And Sum(L.Cost) > TT1.Costs
There are a few issues with this example bar the misuse of the aggregate (commented out) the following works.
Note for my convenience I've added an _ to the table names.
:-
DROP TABLE IF EXISTS Table_1;
DROP TABLE IF EXISTS Table_2;
DROP TABLE If EXISTS temp.temp_table1;
CREATE TABLE Table_1 (ID INTEGER PRIMARY KEY, cost REAL);
CREATE TABLE Table_2 (ID INTEGER PRIMARY KEY, name TEXT);
INSERT INTO Table_1 (cost) VALUES (100.45),(56.78),(99.99);
INSERT INTO Table_2 (name) VALUES ('mike'),('mike'),('fred');
CREATE TEMP TABLE temp_table1 AS
SELECT L.ID,
sum(L.cost)/2 as Costs
FROM Table_1 L
JOIN Table_2 C ON L.ID = C.ID
WHERE C.name = 'mike'
GROUP BY L.ID;
SELECT
count(L.ID)
FROM Table_1 L
JOIN temp_table1 TT1 ON L.ID = TT1.[L.ID]
WHERE
L.ID NOT IN (TT1.[L.ID])
-- AND Sum(L.cost) > TT1.costs --<<<< misuse of aggregate
The issues are based upon the column name being L.ID so this has to be enclosed (rules here SQL As Understood By SQLite - SQLite Keywords apply) [ and ] have been used above.
of course you could circumvent the need for enclosure by naming the column using AS e..g SELECT
L.ID AS lid, --<<<< AS lid ADDED
SUM(L.cost)/2 as Costs, ,.......
Adding the following may be suitable for getting around the misuse of aggregate :-
GROUP BY L.ID
HAVING sum(L.cost) > TT1.costs
Adding the following to the end of the script :-
SELECT
count(L.ID), *
FROM Table_1 L
JOIN temp_table1 TT1 ON L.ID = TT1.[L.ID];
results in :-
If this is only to be used by one SELECT statement then you can use the WITH clause:
WITH TmpTable(id,cost) AS
(
...SELECT statement that returns the two columns (id and cost)...
)
SELECT id, cost FROM TmpTable WHERE ...;

Return distinct pairs of names which have the same exact items in column

I want to find the distinct pairs of names in the table which have the same exact items in the items column. For instance:
CREATE TABLE t
(
name VARCHAR(255),
item VARCHAR(255)
);
INSERT INTO t VALUES("Alice", "Orange");
INSERT INTO t VALUES("Alice", "Pear");
INSERT INTO t VALUES("Alice", "Lemon");
INSERT INTO t VALUES("Bob", "Orange");
INSERT INTO t VALUES("Bob", "Pear");
INSERT INTO t VALUES("Bob", "Lemon");
INSERT INTO t VALUES("Charlie", "Pear");
INSERT INTO t VALUES("Charlie", "Lemon");
The answer here would be Alice,Bob because they took the exact same items.
I want to do it with double negation (using NOT EXISTS/NOT IN) only which I think is more well-suited to this question, but I couldn't come up with anything that is remotely close to being functional.
This is somewhat similar to this question but I'm using SQLite so I cannot use GROUP_CONCAT() but I was wondering how it would be done using relational division using NOT EXISTS/NOT IN.
To get the number of common items between all pairs of names you can use the following query:
SELECT t1.name AS name1, t2.name AS name2, COUNT(*) AS cnt
FROM t AS t1
INNER JOIN t AS t2 ON t1.item = t2.item AND t1.name < t2.name
GROUP BY t1.name, t2.name
Output:
name1 name2 cnt
------------------------
Alice Bob 3
Alice Charlie 2
Bob Charlie 2
Now all you want is to filter out (name1, name2) pairs having a count that is not equal to the number of items of name1 and name2. You can do this using a HAVING clause with correlated subqueries:
SELECT t1.name AS name1, t2.name AS name2
FROM t AS t1
INNER JOIN t AS t2 ON t1.item = t2.item AND t1.name < t2.name
GROUP BY t1.name, t2.name
HAVING COUNT(*) = (SELECT COUNT(*) FROM t WHERE name = t1.name) AND
COUNT(*) = (SELECT COUNT(*) FROM t WHERE name = t2.name)
Demo here
With compound queries:
SELECT t1.name, t2.name
FROM t AS t1, t AS t2
GROUP BY t1.name, t2.name
HAVING t1.name < t2.name
AND NOT EXISTS (SELECT item FROM t WHERE name = t1.name
EXCEPT
SELECT item FROM t WHERE name = t2.name)
AND NOT EXISTS (SELECT item FROM t WHERE name = t2.name
EXCEPT
SELECT item FROM t WHERE name = t1.name);
Using NOT IN is possible, bit expresses exactly the same mechanism with more complexity:
SELECT t1.name, t2.name
FROM t AS t1, t AS t2
GROUP BY t1.name, t2.name
HAVING t1.name < t2.name
AND NOT EXISTS (SELECT item
FROM t
WHERE name = t1.name
AND item NOT IN (SELECT item
FROM t
WHERE name = t2.name))
AND NOT EXISTS (SELECT item
FROM t
WHERE name = t2.name
AND item NOT IN (SELECT item
FROM t
WHERE name = t1.name));
This seems to be working with SQLLite
select t1.name
from t t1
join t t2 on t1.name <> t2.name and t1.item = t2.item
join (select name, count(*) as cnt from t group by name) t3 on t3.name = t1.name
join (select name, count(*) as cnt from t group by name) t4 on t4.name = t2.name
group by t1.name, t3.cnt, t4.cnt
having count(*) = max(t3.cnt, t4.cnt)
I might have found a solution to your issue. Mine was tested using MySQL, but it's not using GROUP_CONCAT(). It might work for your SQLite database. My query is used to find people who have bought the same exact items.
Try using this statement:
SELECT DISTINCT e1.name, e2.name from t e1, t e2 WHERE e1.item=e2.item AND e1.name != e2.name GROUP BY e1.item HAVING count(*) >1;
https://gyazo.com/5e5e9d0ddfb33cb47439a674297108ed

Resources