Sequelize : how to join on foreign keys - join

I'm using Sequelize and Node.js, and I need to join 2 tables on 2 foreign keys,
tableA.hasOne(tableB, { foreignKey: 'fk_tableB' });
tableB.belongsTo(tableA, {foreignKey: 'fk_tableB' });
(by the way, I don't understand the functionality of "targetKey")
but I can only obtain a join on tableA.primaryKey = tableB.fk_tableB.
How can I replace the tableA.primaryKey by tableA.fk_tableA?
I also tried to define twice tableA : in 2 different structures (one with the real primary key and the other with fk_tableA as primary key), but it's also not working (because I need the real tableA mode in another place).
Has someone an idea? Is it a bug from Sequelize?

How can I replace the tableA.primaryKey by tableA.fk_tableA?
There is no tableA.fk_tableA. But if there were, we would expect you to have named it that because column tableA.fk_tableA is a FK to a key column in tableA. Because that's the convention for naming a column fk_tableA. Similarly we would expect a belongTo like yours that adds a column that is a FK to the tableA PK to call it fk_tableA, not fk_tableB. Just like your hasOne gives tableA a column fk_tableB to the tableB PK. (If you want a FK to be to some other column than the PK then you say so via targetKey.)
If you so named FKs after their target table, you seem to want tableA.fk_tableB = tableB.fk_tableA. The way you have named them now, you seem to want tableA.fk_tableB = tableB.fk_tableB.
I need to join 2 tables on 2 foreign keys
It is extremely unlikely that you need the join above. Declaring a column to be a FK says that a value of the source/referencing column is always a value of the target/referenced column. Here targets are PKs. Such a join on a FK to one table and a FK to another table will only return rows that share the same PK value, even though the PKs are from different tables.

Related

Unable to find column names in a FK constraint

I have created two tables in Snowflake.
create or replace TRANSIENT TABLE TESTPARENT (
COL1 NUMBER(38,0) NOT NULL,
COL2 VARCHAR(16777216) NOT NULL,
COL3 VARCHAR(16777216) NOT NULL,
constraint UNIQ_COL3 unique (COL3)
);
create or replace TRANSIENT TABLE TESTCHILD3 (
COL_A NUMBER(38,0) NOT NULL,
COL_B NUMBER(38,0) NOT NULL,
ABCDEF VARCHAR(16777216) NOT NULL,
constraint FKEY_1 foreign key (COL_A, COL_B) references TEST_DB.PUBLIC.TESTPARENT1(COL1,COL2),
constraint FKEY_2 foreign key (ABCDEF) references TEST_DB.PUBLIC.TESTPARENT(COL3)
);
Now I want to execute a query and see the names of columns that are involved in FKEY_2 FOREIGN KEY
in Table TESTCHILD3, but it seems like there are no DB Table/View that keeps this information. I can find out the column names for UNIQUE KEY & PRIMARY KEY but there is nothing for FOREIGN KEYS.
EDIT
I have already tried INFORMATION_SCHEMA.TABLE_CONSTRAINTS, along with INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS and all the other system tables. No luck. Only DESC TABLE is giving me some info related to CONSTRAINTS and COLUMNS but that also has FOREIGN KEY CONSTRAINTS information missing.
SHOW IMPORTED KEYS IN TABLE <fk_table_name>;
Updated answer:
I was checking on something unrelated and noticed a very efficient way to list all primary and foreign keys:
show exported keys in account; -- Foreign keys
show primary keys in account;
When you limit the call to a table, it appears you have to request the foreign keys that point to the parent table:
show exported keys in table "DB_NAME"."SCHEMA_NAME"."PARENT_TABLE";
You can check the documentation for how to limit the show command to a specific database or schema, but this returns rich information in a table very quickly.
maybe you can try to query this view: INFORMATION_SCHEMA.TABLE_CONSTRAINTS
Note: TABLE_CONSTRAINTS only displays objects for which the current role for the session has been granted access privileges.
For more see: https://docs.snowflake.net/manuals/sql-reference/info-schema/table_constraints.html

Override join 'ON' primary column in Criteria

I am using Criteria and I want to explicitly tell hibernate to join on specific columns rather than the primary and foreign key columns. My code is
CreateCriteria<Application>("Application")
.CreateCriteria("Application.Address", "Address", JoinType.FullJoin)
The query that runs for this is
Select * from Application app Full Join Address add ON app.AdressId = add.Id
I want the query to be as
Select * from Application app Full Join Address add ON app.PersonId = add.PersonId
I changed my criteria to
CreateCriteria<Application>("Application")
.CreateCriteria("Application.Address", "Address", JoinType.FullJoin, Restrictions.eqProperty("Application.Person.Id", "Address.Person.Id"))
The sql query that runs is
Select * from Application app Full Join Address add ON app.PersonId = add.PersonId and app.AdressId = add.Id
Is there a way I can explicitly mention in the criteria on the joins I wish to have ?
NHibernate always does joins basing on foreign keys.
I guess that you have configured relationship between Application and Address entities via Application.AddressId foreign key.
That's why you have app.AdressId = add.Id in join.
If I'm right you cannot do the join on some other property.
You can find a slower workaround with subqueries or write a sql query manually.
See other answers:
https://stackoverflow.com/a/28689750/5126411
https://stackoverflow.com/a/44924773/5126411

How to join this tables properly?

I have 2 tables, one is for the categories and the other is for the particulars of the corresponding categories.
I need all categories to be shown with their corresponding particulars.
I am not clear about your question and i assume you are using sql server
select t1.category,t2.particularname from categories t1 left join particulars t2 on t1.categoryid=t2.categoryid
From what you said, it seems Categories table has one-to-many relation with particulars. That means Primary Key of Categories should be refered in particulars as Foreign Key in Particulars. You can use following Query to join
SELECT * FROM categories C JOIN particulars P ON C.cat_id = P.cat_id;
And you should rename if some other columns in both tables have same name and you don't want them to be a part of JOIN.
For more example on JOIN.

How to migrate complex Rails database to use UUID primary keys Postgresql

I have a database I would like to convert to use UUID's as the primary key in postgresql.
I have roughly 30 tables with deep multi-level associations. Is there an 'easy' way to convert all current ID's to UUID?
From this: https://coderwall.com/p/n_0awq, I can see that I could alter the table in migration. I was thinking something like this:
for client in Client.all
# Retrieve children
underwritings = client.underwritings
# Change primary key
execute 'ALTER TABLE clients ALTER COLUMN id TYPE uuid;'
execute 'ALTER TABLE clients ALTER COLUMN id SET DEFAULT uuid_generate_v1();'
# Get new id - is this already generated?
client_id = client.id
for underwriting in underwritings
locations = underwriting.locations
other_record = underwriting.other_records...
execute 'ALTER TABLE underwritings ALTER COLUMN id TYPE uuid;'
execute 'ALTER TABLE underwritings ALTER COLUMN id SET DEFAULT uuid_generate_v1();'
underwriting.client_id = client_id
underwriting.saved
underwriting_id = underwriting.id
for location in locations
buildings = location.buildings
execute 'ALTER TABLE locations ALTER COLUMN id TYPE uuid;'
execute 'ALTER TABLE locations ALTER COLUMN id SET DEFAULT uuid_generate_v1();'
location.undewriting_id = underwriting_id
location.save
location_id = location.id
for building in buildings
...
end
end
for other_record in other_records
...
end
...
...
end
end
Questions:
Will this work?
Is there an easier way to do this?
Will child records be retrieved properly as long as they are retrieved before the primary key is changed?
Will the new primary key be already generated as soon as the alter table is called?
Thanks very much for any help or tips in doing this.
I found these to be quite tedious. It is possible to use direct queries to PostgreSQL to convert table with existing data.
For primary key:
ALTER TABLE students
ALTER COLUMN id DROP DEFAULT,
ALTER COLUMN id SET DATA TYPE UUID USING (uuid(lpad(replace(text(id),'-',''), 32, '0'))),
ALTER COLUMN id SET DEFAULT uuid_generate_v4()
For other references:
ALTER TABLE students
ALTER COLUMN city_id SET DATA TYPE UUID USING (uuid(lpad(replace(text(city_id),'-',''), 32, '0')))
The above left pads the integer value with zeros and converts to a UUID. This approach does not require id mapping and if needed old id could be retrieved.
As there is no data copying, this approach works quite fast.
To handle these and more complicated case of polymorphic associations please use https://github.com/kreatio-sw/webdack-uuid_migration. This gem adds additional helpers to ActiveRecord::Migration to ease these migrations.
I think trying to do something like this through Rails would just complicate matters. I'd ignore the Rails side of things completely and just do it in SQL.
Your first step is grab a complete backup of your database. Then restore that backup into another database to:
Make sure that your backup works.
Give you a realistic playpen where you can make mistakes without consequence.
First you'd want to clean up your data by adding real foreign keys to match all your Rails associations. There's a good chance that some of your FKs will fail, if they do you'll have to clean up your broken references.
Now that you have clean data, rename all your tables to make room for the new UUID versions. For a table t, we'll refer to the renamed table as t_tmp. For each t_tmp, create another table to hold the mapping from the old integer ids to the new UUID ids, something like this:
create table t_id_map (
old_id integer not null,
new_id uuid not null default uuid_generate_v1()
)
and then populate it:
insert into t_id_map (old_id)
select id from t_tmp
And you'll probably want to index t_id_map.old_id while you're here.
This gives us the old tables with integer ids and a lookup table for each t_tmp that maps the old id to the new one.
Now create the new tables with UUIDs replacing all the old integer and serial columns that held ids; I'd add real foreign keys at this point as well; you should be paranoid about your data: broken code is temporary, broken data is usually forever.
Populating the new tables is pretty easy at this point: simply use insert into ... select ... from constructs and JOIN to the appropriate t_id_map tables to map the old ids to the new ones. Once the data has been mapped and copied, you'll want to do some sanity checking to make sure everything still makes sense. Then you can drop your t_tmp and t_id_map tables and get on with your life.
Practice that process on a copy of your database, script it up, and away you go.
You would of course want to shut down any applications that access your database while you're doing this work.
Didn't want to add foreign keys, and wanted to to use a rails migration. Anyways, here is what I did if others are looking to do this (example for 2 tables, I did 32 total):
def change
execute 'CREATE EXTENSION "uuid-ossp";'
execute <<-SQL
ALTER TABLE buildings ADD COLUMN guid uuid DEFAULT uuid_generate_v1() NOT NULL;
ALTER TABLE buildings ALTER COLUMN guid SET DEFAULT uuid_generate_v1();
ALTER TABLE buildings ADD COLUMN location_guid uuid;
ALTER TABLE clients ADD COLUMN guid uuid DEFAULT uuid_generate_v1() NOT NULL;
ALTER TABLE clients ALTER COLUMN guid SET DEFAULT uuid_generate_v1();
ALTER TABLE clients ADD COLUMN agency_guid uuid;
ALTER TABLE clients ADD COLUMN account_executive_guid uuid;
ALTER TABLE clients ADD COLUMN account_representative_guid uuid;
SQL
for record in Building.all
location = record.location
record.location_guid = location.guid
record.save
end
for record in Client.all
agency = record.agency
record.agency_guid = agency.guid
account_executive = record.account_executive
record.account_executive_guid = account_executive.guid unless account_executive.blank?
account_representative = record.account_representative
record.account_representative_guid = account_representative.guid unless account_representative.blank?
record.save
end
execute <<-SQL
ALTER TABLE buildings DROP CONSTRAINT buildings_pkey;
ALTER TABLE buildings DROP COLUMN id;
ALTER TABLE buildings RENAME COLUMN guid TO id;
ALTER TABLE buildings ADD PRIMARY KEY (id);
ALTER TABLE buildings DROP COLUMN location_id;
ALTER TABLE buildings RENAME COLUMN location_guid TO location_id;
ALTER TABLE clients DROP CONSTRAINT clients_pkey;
ALTER TABLE clients DROP COLUMN id;
ALTER TABLE clients RENAME COLUMN guid TO id;
ALTER TABLE clients ADD PRIMARY KEY (id);
ALTER TABLE clients DROP COLUMN agency_id;
ALTER TABLE clients RENAME COLUMN agency_guid TO agency_id;
ALTER TABLE clients DROP COLUMN account_executive_id;
ALTER TABLE clients RENAME COLUMN account_executive_guid TO account_executive_id;
ALTER TABLE clients DROP COLUMN account_representative_id;
ALTER TABLE clients RENAME COLUMN account_representative_guid TO account_representative_id;
SQL
end

Inserting data as foreign key constraint in grails

Continuing to the post of reading-from-XML-and-storing-the-values-in-database-using-grails
am facing an another problem in it. As the employee id is related to other tables as foreign key how am suppose to insert the data into the database without conflict what i want to add in grails code to avoid the below displayed error.
com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException:Cannot
add or update a child rowL a foreign key constraint fails.. ( CONSTRAINT 'EMPLOYEE_ID_HEADER'
FOREIGN KEY ('EMPLOYEE_ID') REFERENCES 'employee_header'('employee_id'))
here employee_id is a column and employee_header is a separate table that contains employee_id as a foreign key.
Finally I got the answer, when you are inserting the values in database the related foreign key tables will get affects initially. So to handle this situation we have to insert the foreign key related table at first then the table we need to add exactly.
For the reference, at first I had given the code like this
sql.executeInsert("insert into order_item (order_id,product_id,
order_item_seq_id) values (${order_id},${product_id},${order_item_seq_id})")
sql.executeInsert("insert into product(product_id) values(${product_id})")
This first inserts the order_item table then the foreignkey constraint table product, so the data did not inserted. So the correct code is
sql.executeInsert("insert into order_header(order_id) values(${order_id})")
sql.executeInsert("insert into product(product_id) values(${product_id})")
sql.executeInsert("insert into order_item (order_id,product_id,
order_item_seq_id) values (${order_id},${product_id},${order_item_seq_id})")
Now this inserts the data successfully without errors.

Resources