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?
Related
How do I select the highest number in one column and then search the the ID in other table?
I have these two tables:
create table armazens_has_artigos
(
stockMinimo int not null,
stockQuantidade int not null,
idArtigos int not null,
idArmazens int not null,
idZonasFisicas int not null
constraint fk_armazens_has_artigos_idArtigos
foreign key (idArtigos)
references artigos (idArtigos) on delete cascade,
constraint fk_armazens_has_artigos_idArmazens
foreign key (idArmazens)
references armazens (idArmazens) on delete cascade,
constraint fk_armazens_has_artigos_zonasFisicas_idZonasFisicas
foreign key (idZonasFisicas)
references zonasFisicas (idZonasFisicas) on delete cascade
);
In this one I need to select the highest StockQuantidade and then with the same id select in this table the nome
create table artigos
(
idArtigos int primary key,
nome varchar (45) not null,
descr varchar (45) not null,
precoCompra float not null,
precoVenda float not null,
unidadeRepresentacao varchar (45) not null
);
There might be more then one Artigos matching the criteria:
SELECT *
FROM artigos
WHERE idArtigos IN
(
SELECT idArtigos
FROM dbo.armazens_has_artigos
WHERE stockQuantidade =
(
SELECT MAX(stockQuantidade)FROM armazens_has_artigos
)
);
SELECT
nome
FROM artigos
JOIN armazens_has_artigos
ON armazens_has_artigos.idArtigos = artigos.idArtigos
WHERE armazens_has_artigos.stockQuantidade = (SELECT
MAX(stockQuantidade)
FROM armazens_has_artigos)
i have atable such as codes table have these values:
Major_code
minor_code
name
1
0
Country
1
1
USA
1
2
JOR
2
0
Food
2
1
Burger
2
2
Mansaf
I need to create table have these columns (ID, Country_ID , Food_ID) what is the best constraint that resolve this issue?
Having a single lookup table for all reference codes is a very poor design, to the point of almost being a SQL anti-pattern. It is likely to have performance issues. It is also difficult to ensure that the value of COUNTRY_MINOR_CODE references an actual country minor code in the reference table. Which brings us to this statement:
I need to create table have these columns (ID, Country_ID , Food_ID)
Can't be done. Or at least it can be done but you can't build foreign keys on that, because minor keys are not unique in the referenced table. But if you are set on a single table, say lookup_table with primary key defined on (major_code, minor code), you need this:
create table country_food (
id number primary_key
,country_major_code number not null
,country_minor_code number not null
,food_major_code number not null
,food_minor_code number not null
,constraint country_major_code_ck check (country_major_code = 1)
,constraint country_minor_code_ck check (country_minor_code != 0)
,constraint food_major_code_ck check (country_major_code = 2)
,constraint food_minor_code_ck check (country_minor_code != 0)
,constraint country_code_fk foreign key (country_major_code, country_minor_code) references lookup_table
,constraint food_code_fk foreign key (food_major_code, food_minor_code) references lookup_table
)
/
The hard coding of values, the additional check constraints, the increased complexity of joining to look up the name ... These are all reasons why you should choose to have separate tables for FOOD, COUNTRY, etc.
create table country (
country_id number primary_key
,country_name varchar2(24) not null
)
/
create table food (
food_id number primary_key
,food_name varchar2(24) not null
)
/
create table country_food (
id number primary_key
,country_id number not null
,food_id number not null
,food_major_code number not null
,constraint country_code_fk foreign key (country_id) references country
,constraint food_code_fk foreign key (food_id) references food
)
/
If I've got it right, you can get all Country+Food pairs with the query
select t1.minor_code counrty_id, t2.minor_code food_id
from (
select minor_code
from codesTable
where Major_code = (
select c.Major_code
from codesTable c
where c.minor_code=0 and c.name='Country')
) t1
cross join (
select minor_code
from codesTable
where Major_code = (
select c.Major_code
from codesTable c
where c.minor_code=0 and c.name='Food')
) t2
You can use the query to insert data into a table with an autogenerated ID or use it any other way.
First you need to consider the design of the tables
Table of country
Table of foods
Tables of food_country -------- relationship => many-to-many
CREATE TABLE Country
(
Major_code numeric(15) not null,
minor_code numeric(15),
name varchar(50),
CONSTRAINT country_pk PRIMARY KEY (Major_code)
);
CREATE TABLE Food
(
Food_ID numeric(15) not null,
//...........................
name varchar(50),
CONSTRAINT food_pk PRIMARY KEY (Food_ID)
);
CREATE TABLE Counry_Food
(
ID numeric(10) not null,
Country_ID numeric(15) not null,
Food_ID numeric(15) not null,
CONSTRAINT fk_country
FOREIGN KEY (Country_ID)
REFERENCES Country(Major_code),
CONSTRAINT fk_food
FOREIGN KEY (Food_ID)
REFERENCES supplier(Food_ID),
);
I have the following tables that have profiles and list of photos (in the photo table) for each profile. I also have a service table and a used table, I want a query that would return the profile id which does not belong in the used service and also has more than 3 photos in the photo table
The profile table
CREATE TABLE public.profile
(
id integer NOT NULL DEFAULT nextval('profile_id_seq'::regclass),
name text COLLATE pg_catalog."default" NOT NULL,
birthday timestamp with time zone NOT NULL,
CONSTRAINT profile_id PRIMARY KEY (id)
)
The photo table
CREATE TABLE public.photo
(
id integer NOT NULL DEFAULT nextval('photo_id_seq'::regclass),
image bytea NOT NULL,
image_id text COLLATE pg_catalog."default" NOT NULL,
order_count smallint NOT NULL,
profile_id bigint NOT NULL,
CONSTRAINT photo_id PRIMARY KEY (id),
CONSTRAINT photo_profile_id_fkey FOREIGN KEY (profile_id)
REFERENCES public.profile (id) MATCH SIMPLE
ON UPDATE CASCADE
ON DELETE CASCADE
)
the service table
CREATE TABLE public.service
(
id integer NOT NULL DEFAULT nextval('service_id_seq'::regclass),
name text COLLATE pg_catalog."default" NOT NULL,
CONSTRAINT service_id PRIMARY KEY (id)
)
the used table
CREATE TABLE public.used
(
id integer NOT NULL DEFAULT nextval('used_id_seq'::regclass),
service_id bigint NOT NULL,
profile_id bigint NOT NULL,
insert_timestamp timestamp with time zone NOT NULL DEFAULT now(),
CONSTRAINT used_id PRIMARY KEY (id),
CONSTRAINT used_profile_id_fkey FOREIGN KEY (profile_id)
REFERENCES public.profile (id) MATCH SIMPLE
ON UPDATE CASCADE
ON DELETE CASCADE,
CONSTRAINT used_service_id_fkey FOREIGN KEY (service_id)
REFERENCES public.service (id) MATCH SIMPLE
ON UPDATE CASCADE
ON DELETE CASCADE
)
use exists and not exists
select p.* from profile p
where exists ( select 1 from photo ph where ph.profile_id =p.id
having count (distinct image_id )=3
)
and not exists ( select 1 from used u where u.profile_id =p.id)
I would go for:
select p.profile_id
from photo p
where not exists (select 1
from used u
where u.profile_id = p.profile_id
)
group by p.profile_id
having count(*) >= 3;
If you just need the profile_id, then the profiles table is not needed.
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;
I would need if I could help with the following code:
CREATE TABLE `car` (
`car_id` int(10) unsigned NOT NULL auto_increment,
`car_name` varchar(25) NOT NULL,
`car_year` date NOT NULL,
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB;
CREATE TABLE `people` (
`peo_id` int(10) unsigned NOT NULL auto_increment,
`peo_name` varchar(50) NOT NULL,
`peo_surname` varchar(200) NOT NULL,
`car_id` int(10) unsigned NOT NULL,
PRIMARY KEY (`fav_id`),
KEY `FK_Favorites` (`user_id`),
CONSTRAINT `FK_Favorites` FOREIGN KEY (`car_id`) REFERENCES `car` (`car_id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB;
I have questions, show all cars that are more than 3 people ordered by year of car.
Thank you very much, sorry for my bad English
SELECT
c.car_id
,c.car_year
,COUNT(p.peo_id)
FROM car c
INNER JOIN people p on c.card_id = p.car_id
GROUP BY c.car_id ,c.car_year
HAVING COUNT(p.peo_id) > 3
ORDER BY c.car_year
First, find number of cars with more than three people
select car_id
from people
group by car_id
having count(*) > 3
Now, get the car details
select * from car
where car_id in
(
select car_id
from people
group by car_id
having count(*) > 3
)
order by car_year