Let's say we have the following tables
CREATE TABLE IF NOT EXISTS table1 (
id int NOT NULL AUTO_INCREMENT,
name varchar(64)
);
CREATE TABLE IF NOT EXISTS table2 (
id int NOT NULL AUTO_INCREMENT,
name varchar(64)
);
CREATE TABLE IF NOT EXISTS table3 (
id int NOT NULL AUTO_INCREMENT,
name varchar(64)
);
CREATE TABLE IF NOT EXISTS table1_table2 (
table1Id int NOT NULL,
table2Id int NOT NULL
FOREIGN KEY (table1Id) REFERENCES table1 (id),
FOREIGN KEY (table2Id) REFERENCES table2 (id),
);
CREATE TABLE IF NOT EXISTS table1_table3 (
table1Id int NOT NULL,
table3Id int NOT NULL
FOREIGN KEY (table1Id) REFERENCES table1 (id),
FOREIGN KEY (table3Id) REFERENCES table3 (id),
);
Afterwards, I insert one value in table1, as well as multiple into table2 and table3 (with inserts which link the entries with the tables table1_table2 and table1_table3).
If I try to select everything from the 'non-linking' tables now, I'll get something similar to
[
{
table1.id: 1,
table1.name: 'my-table',
table2.id: 1,
table2.name: 'other-table',
table3.id: 1,
table3.name: 'something else'
},
{
table1.id: 1,
table1.name: 'my-table',
table2.id: 2,
table2.name: 'new-other-table',
table3.id: 1,
table3.name: 'something else'
}
<...>
]
This way the values for table1 and table3 will be repeated multiple times, without any change. It would make much more sense if the values could be combined or clustered in a way. For example:
[
{
table1.id: 1,
table1.name: 'my-table',
table2 : [
{
table2.id: 1,
table2.name: 'other-table',
}, <...>
],
table3 : [
{
table3.id: 1,
table3.name: 'something else'
}, <...>
]
}
]
Is there a query or function with which I can achieve this?
Related
Given three related tables I would like to find out for given filter criteria what was the last change date and who did these changes.
Example: Three tables, where A <1--n> B <1--n> C
CREATE TABLE table_a
(
a_id bigint not null,
some_data varchar(255),
last_changed_at timestamp not null,
last_changed_by_id bigint not null,
primary key (a_id)
);
CREATE TABLE table_b
(
b_id bigint not null,
a_id bigint not null,
some_data varchar(255),
last_changed_at timestamp not null,
last_changed_by_id bigint not null,
primary key (b_id)
);
CREATE TABLE table_c
(
c_id bigint not null,
b_id bigint not null,
a_id bigint not null,
some_data varchar(255),
last_changed_at timestamp not null,
last_changed_by_id bigint not null,
primary key (c_id)
);
alter table table_b add constraint table_a_fk foreign key (a_id) references table_a;
alter table table_c add constraint table_a_fk foreign key (a_id) references table_a;
alter table table_c add constraint table_b_fk foreign key (b_id) references table_b;
We would like to see among all three tables (table_a, table_b, table_c) what was the last range done related to a_id and by whom given conditions on table_a (e.g. WHERE table_a.some_data like 'abc%').
As we are using Hibernate Envers for all three tables we have also audit tables available defined as:
CREATE TABLE table_a_aud
(
rev bigint not null,
revtype smallint not null,
revend bigint not null,
a_id bigint not null,
some_data varchar(255),
last_changed_at timestamp not null,
last_changed_by_id bigint not null,
primary key (a_id, rev)
);
CREATE TABLE table_b_aud
(
rev bigint not null,
revtype smallint not null,
revend bigint not null,
b_id bigint not null,
a_id bigint not null,
some_data varchar(255),
last_changed_at timestamp not null,
last_changed_by_id bigint not null,
primary key (b_id, rev)
);
CREATE TABLE table_c_aud
(
rev bigint not null,
revtype smallint not null,
revend bigint not null,
c_id bigint not null,
b_id bigint not null,
a_id bigint not null,
some_data varchar(255),
last_changed_at timestamp not null,
last_changed_by_id bigint not null,
primary key (c_id, rev)
);
CREATE TABLE revinfo
(
rev bigint not null,
revtstmp bigint,
last_changed_at timestamp not null,
last_changed_by_id bigint not null,
primary key (rev)
);
alter table table_a_aud add constraint revinfo_a_fk foreign key (rev) references revinfo;
alter table table_b_aud add constraint revinfo_b_fk foreign key (rev) references revinfo;
alter table table_c_aud add constraint revinfo_c_fk foreign key (rev) references revinfo;
I have tried the following variants, but I am not sure if one of these is the best approach.
Union with all three tables, window function to pick the newest.
Union with all three revision tables (have much more data), window function to pick the newest
Create a new table with the latest changed by a_id, last_changed_at, last_changed_by_id filled with either (a) a trigger or (b) by our application.
Variant 1 may look like:
SELECT tab_a.*, lc.last_changed_at, lc.last_changed_by_id
FROM table_a tab_a
JOIN (
(SELECT a_id, last_changed_at, last_changed_by_id
FROM (
SELECT a_id,
last_changed_at,
last_changed_by_id,
ROW_NUMBER() OVER (PARTITION BY unioned.a_id ORDER BY unioned.last_changed_at DESC) AS rk
FROM (
(SELECT a_id, last_changed_at, last_changed_by_id FROM table_a)
UNION ALL
(SELECT a_id, last_changed_at, last_changed_by_id FROM table_b)
UNION ALL
(SELECT a_id, last_changed_at, last_changed_by_id FROM table_c)) unioned) unioned_with_rank
WHERE unioned_with_rank.rk = 1)) lc ON tab_a.a_id = lc.a_id
WHERE tab_a.some_data like 'abc%';
Although adding indexes on table_a, table_b and table_c Postgres is still doing a seq scan on all tables. Union and then using a window function may not be the best from a performance perspective.
Variant 2 may look like:
SELECT tab_a.*, lc.last_changed_at, lc.last_changed_by_id
FROM table_a tab_a
JOIN (
SELECT a_id, MAX(rev) rev
FROM (SELECT a_id, rev
FROM table_a_aud
UNION ALL
SELECT a_id, rev
FROM table_b_aud
UNION ALL
SELECT a_id, rev
FROM table_c_aud) unioned
GROUP BY a_id) uniongrouped ON tab_a.a_id = uniongrouped.a_id
JOIN revinfo_ lc ON uniongrouped.rev = lc.rev;
WHERE tab_a.some_data like 'abc%';
We have still a union, but we could have a sorted index on (a_id, rev DESC) which might help. But *_aud tables are containing > 10 x more data than the other tables. IMO this may still be the better approach to use this one.
Variant 3
Implemented in our application, no special database query needed. Result can be a simple join:
SELECT tab_a.*, lc.last_changed_at, lc.last_changed_by_id
FROM table_a tab_a JOIN last_change lc ON tab_a.a_id = lc.a_id
This would make the query statements much faster, but we need to distribute update code all over the application code, which I don't like. Also, I see no trigger which can be executed shortly before the transaction ends and only adding one insert/update statement instead of multiples if many entries have been added/updated on tables table_a, table_b and table_c in the same transaction.
Does anyone has done something similar?
What is the best approach to solving this requirement?
Is there a generic approach that works for PostgreSQL, SQL Server and maybe H2 and Oracle?
These are tables in SQLite: especie_similar
CREATE TABLE especie_similar (
id_especie INTEGER NOT NULL,
id_similar INTEGER NOT NULL,
PRIMARY KEY (
id_especie,
id_similar
),
FOREIGN KEY (
id_especie
)
REFERENCES especie (id_especie) ON DELETE CASCADE,
FOREIGN KEY (
id_similar
)
REFERENCES especie (id_especie) ON DELETE CASCADE
);
And especie table:
CREATE TABLE especie (
id_especie INTEGER PRIMARY KEY AUTOINCREMENT
NOT NULL,
nombre_especie VARCHAR NOT NULL,
cient_especie VARCHAR NOT NULL,
desc_especie TEXT,
nidificacion TEXT NOT NULL,
dimorfismo INTEGER NOT NULL,
genero_id INTEGER NOT NULL,
endemismo_id INTEGER NOT NULL,
abundancia_id INTEGER NOT NULL,
FOREIGN KEY (
genero_id
)
REFERENCES genero (id_genero),
FOREIGN KEY (
endemismo_id
)
REFERENCES endemismo (id_endemismo),
FOREIGN KEY (
abundancia_id
)
REFERENCES abundancia (id_abundancia)
);
My situation: Table especie has multiple datas and I want to insert in especie_similar ids from table especie.
How can i code a trigger or sql statement checking if id_especie and id_similar are not equals?
How can i code to retrieve all especies distinct of target id
(ex: table especie_similar has id_especie=1 and id_similar=2
indicating that species id 1 is similar to another species id 2 but
cannot insert id_especie=2 and id_similar=1 due to previous
duplicate data)?
Thanks and sorry my english is not good.
For question #1 you can use a standard SQL constraint:
create table especie_similar (
...
check (id_especie <> id_similar) -- your condition here
);
For question #2 you can use a standard "Recursive CTE". See SQLite WITH Clause. Example:
with recursive node as (
select id from especie_similar
where id_especie = 123 -- first node criteria here
union all
select s.id_especie
from node n
join especie_similar s on s.id_similar = n.id
)
select * from node;
There is a query:
select * from
(
select
p.ID
from
MY_PRODUCT_PARENT_LINK pLink
inner join MY_PRODUCT p on pLink.FK_PARENT=p.ID
order by
pLink.DESCENDANT_LVL desc
)
where rownum <= 1
;
The inner query returns one row with not null value of p.ID.
The outer query - one row with null value of p.ID. Here expected not null value.
The database sever is Oracle 11g.
There is a code to reproduction:
CREATE TABLE MY_PRODUCT
(
FK_PARENT NUMBER(19),
ID NUMBER(19) NOT NULL,
NAME VARCHAR2(4000 BYTE),
CONSTRAINT PK_MY_PRODUCT PRIMARY KEY (ID)
);
INSERT INTO MY_PRODUCT (FK_PARENT, ID,NAME) VALUES (null, 111111,'PRODUCT_NAME_01');
INSERT INTO MY_PRODUCT (FK_PARENT, ID,NAME) VALUES (null, 111112,'PRODUCT_NAME_02');
CREATE TABLE MY_PRODUCT_PARENT_LINK
(
ID NUMBER(19) NOT NULL,
FK_PRODUCT NUMBER(19) NOT NULL,
FK_PARENT NUMBER(19) NOT NULL,
DESCENDANT_LVL NUMBER(19) NOT NULL,
primary key (ID)
);
ALTER TABLE MY_PRODUCT_PARENT_LINK ADD
CONSTRAINT MY_PRD_PARENT_TO_MY_PRODUCT
FOREIGN KEY (FK_PRODUCT)
REFERENCES MY_PRODUCT (ID) ON DELETE CASCADE;
ALTER TABLE MY_PRODUCT_PARENT_LINK ADD
CONSTRAINT MY_PRD_PARENT_TO_PARENT
FOREIGN KEY (FK_PARENT)
REFERENCES MY_PRODUCT (ID) ON DELETE CASCADE;
INSERT INTO MY_PRODUCT_PARENT_LINK (ID, FK_PRODUCT, FK_PARENT, DESCENDANT_LVL) VALUES (211111, 111112, 111111, 1);
Try and select the ID in the outer query and see if that returns the result. so instead of select * in the outer do select ID.
Suppose I have three tables:
CREATE TABLE "C" (
id bigint NOT NULL,
cc integer,
CONSTRAINT "C_ID" PRIMARY KEY (id)
);
ALTER TABLE "C" OWNER TO ***;
CREATE TABLE "B" (
id bigint NOT NULL,
c_id bigint references "C"(id),
bb integer,
CONSTRAINT "B_ID" PRIMARY KEY (id)
);
ALTER TABLE "B" OWNER TO ***;
CREATE TABLE "A" (
id bigint NOT NULL,
c_id bigint references "C"(id),
aa integer,
CONSTRAINT "A_ID" PRIMARY KEY (id)
);
ALTER TABLE "A" OWNER TO ***;
And I need to select rows from the table A which have common foreign keys (c_id) with rows from the table B. Though, I have the following SQL query
SELECT "A".aa AS "AA", "B".bb AS "BB", "C".id AS "C_ID"
FROM "A"
INNER JOIN "C" on "C".id = "A".c_id
INNER JOIN "B" on "C".id = "B".c_id;
In Java
#Entity
class A {
...
#ManyToOne
C cObj;
...
}
#Entity
class B {
..
#ManyToOne
C cObj;
..
}
#Entity
class C {
..
}
So question: how can I write an equivalent Criteria API Query?
Thanks.
The parent table is:
CREATE TABLE BHEAD (
ID INTEGER primary key asc,
DESCR TEXT,
LINECTR INT,
UNITCTR INT)
The child table is:
CREATE TABLE BDET (
ID INTEGER primary key asc,
BID INTEGER,
BCODE TEXT,
QTY INTEGER,
FOREIGN KEY (BID) REFERENCES BHEAD(ID) ON DELETE CASCADE
)
I also execute the SQL PRAGMA foreign_keys = ON;.
However, it does not work; when I delete one row from BHEAD, its associated rows in BDET are not gone...
Why was that?
What version of SQLite are you using?
Please see: Foreign Keys.
In order to use foreign key constraints in SQLite, the library must be compiled with neither SQLITE_OMIT_FOREIGN_KEY or SQLITE_OMIT_TRIGGER defined.
However, you can also implement on delete cascade to delete all child
rows when a parent row is deleted.
-- Create the test tables using ON DELETE CASCADE
DROP TABLE t3 PURGE;
--DROP TABLE t2 PURGE;
--DROP TABLE t1 PURGE;
CREATE TABLE t1 (
id NUMBER,
description VARCHAR2(50),
CONSTRAINT t1_pk PRIMARY KEY (id)
);
CREATE TABLE t2 (
id NUMBER,
t1_id NUMBER,
description VARCHAR2(50),
CONSTRAINT t2_pk PRIMARY KEY (id),
CONSTRAINT t2_t1_fk FOREIGN KEY (t1_id) REFERENCES t1 (id) ON DELETE CASCADE
);
CREATE TABLE t3 (
id NUMBER,
t2_id NUMBER,
description VARCHAR2(50),
CONSTRAINT t3_pk PRIMARY KEY (id),
CONSTRAINT t3_t2_fk FOREIGN KEY (t2_id) REFERENCES t2 (id)
);
INSERT INTO t1 VALUES (1, 't1 ONE');
INSERT INTO t2 VALUES (1, 1, 't2 ONE');
INSERT INTO t2 VALUES (2, NULL, 't2 TWO');
INSERT INTO t3 VALUES (1, 1, 't3 ONE');
INSERT INTO t3 VALUES (2, NULL, 't3 TWO');
COMMIT;
SELECT (SELECT COUNT(*) FROM t1) AS t1_count,
(SELECT COUNT(*) FROM t2) AS t2_count,
(SELECT COUNT(*) FROM t3) AS t3_count
FROM dual;
DELETE FROM t3;
rollback;
truncate table t1 ;
rollback;
truncate table t1 CASCADE;