I'm facing an issue with how to set up my MySQL selected relationship table as you can see below.
&
Our users have a profile page, where they can answer to 3 questions from a dropdown menu with multiple choices.
My question is how to add their 3 choices in my MySQL table selected in a single query and where I can update their choices if needed. I can't use ON DUPLICATE KEY since my user_id key is only an index because each user will appear 3 times in selected since there are 3 questions per user.
On their profile page, when our users hit the Submit button I would like our selected table to insert and/or update the choice_id for that user.
I'm using objection JS to set up my models. Here are my User and Selected models
USER MODEL
class User extends Model {
static get tableName() {
return 'users';
}
static get relationMappings() {
return {
choice: {
relation: Model.HasManyRelation,
modelClass: Selected,
join: {
from: 'users.id',
to: 'selected.user_id',
},
},
user: {
relation: Model.ManyToManyRelation,
modelClass: Choices,
join: {
from: 'choices.id',
through: {
from: 'selected.user_id',
to: 'selected.choice_id',
},
to: 'users.id',
},
},
};
}
SELECTED MODEL
class Selected extends Model {
static get tableName() {
return 'selected';
}
static get jsonSchema() {
return {
type: 'object',
properties: {
id: {
type: 'integer',
},
choice_id: {
type: 'integer',
},
user_id: {
type: 'integer',
},
},
};
}
}
Any help to check out if the relationship table is correctly set up or help with the MySQL query would be much appreciated.
Thanks!
EDIT This is where I'm at. Missing the update function for existing answers.
insert into selected(choice_id, user_id)
select choices.Id, users.Id from choices join users
on choices.Id in (1, 6, 10) and users.Id = 91
WHERE NOT EXISTS (
SELECT user_id FROM selected WHERE user_id = 91
) LIMIT 3;
This is the kind of insert you want to execute:
insert into selected(Choice_id, User_id)
select User.Id, Choices.Id
from User
join Choices
on User.Id = 12 and Choices.Id in (4, 8, 15);
Instead of the hard-coded values above, you can use your actual values on the server-side. If you are using PHP, then check what you have in $_POST and $_SESSION and build your query, but make sure you do not allow SQL injection to occur.
EDIT
Sorry for the late reply. You can do the insertion with the criteria of non-existence, like this:
insert into selected(Choice_id, User_id)
select User.Id, Choices.Id
from User
join Choices
on User.Id = 12 and Choices.Id in (4, 8, 15)
where not exists (
select 1
from selected
where Choice_id = Choices.Id and User_id = User.id
);
However, you intend to perform an update if the user chooses a different answer of a question. You can do it like this
update selected
join Choices
on selected.Choice_Id = Choices.Id and Selected.User_id = 12
join Questions
on Choices.Question_id = Questions.Id and Questions.Id = 4
set Choices = 5;
Assuming that you perform updates like above before you perform the insert-select, you should get the result you prefer. Alternatively you could create a trigger before insert which would check whether the pair already exists and if so, it would update instead of insert.
In the end, I'm going to forget the update ability of this, since I believe my selected table wasn't built the 'right' way.
I should have only one row per user_id which lists the 3 choices (choice_id1, choice_id2 and choice_id3). Like that I could use ON DUPLICATE KEY UPDATE for the user_id value.
My workaround is to delete all rows for an user before inserting new values.
START TRANSACTION ;
DELETE FROM
selected
WHERE
user_id = 91;
INSERT INTO selected(choice_id, user_id)
select choices.Id, users.Id from choices join users
on choices.Id in (3, 5, 11) and users.Id = 91
COMMIT ;
Related
I'm using TypeORM with NestJS and I want to do this query to a MySQL DB:
UPDATE user u, table2 t2
SET u.is_active = 0, t2.is_active = 0
WHERE u.id = 'id'
AND t2.user_id = u.id;
Relations are:
User:
#OneToMany(
() => Table2,
(t2) => t2.user,
{
onDelete: 'CASCADE',
eager: true
}
)
field_table_2: Table2[]
And Table2
#ManyToOne(
() => User,
(user) => user.field_table_2
)
#JoinColumn({name:'user_id'})
user: User
The point is: One user can have many table2 but every row in table2 depends for only one user. Then a relation one-to-many and many-to-one. All right so far.
When I delete one user, I don't want remove the data into DB, I want to set field is_active to false in both tables.
I can do it in a single query using SQL but using TypeORM I've only found a solution using raw sql:
async remove(id: string) {
return await this.dataSource.query(`
UPDATE user u, table2 t2
SET u.is_active = 0, t2.is_active = 0
WHERE u.id = ?
AND t2.user_id = u.id`,[id])
}
But having an ORM I don't like to use raw SQL... how can I get the query I want using the ORM?
I know I can create a transaction, update first one table, then the other table and if everything is ok commit the transaction.
But, if I can do it in a single query, is efficient to do it in a transaction? I refer also for simplicity in a single query.
Thanks in advance.
TypeORM does not currently have an expressive way of updating multiple tables in a single query this via either query builder or repository methods. Currently those are limited to updating a single table.
I would say in your case, if you wish to do this in a single query pull out isActive into an entity / table of its own. Then both user and table 2 could have a 1-1 relationship with this new entity. Therefore, you only need to update one table entry.
Otherwise, this can be done with two separate update queries and a transaction. How you will perform the update is more a matter of choice, could use Repository or QueryBuilder.
There are also several options for managing transactions, the most fine grained of which is QueryRunner.
Pseudocode might look something like this:
queryRunner.startTransaction();
try {
await queryRunner
.createQueryBuilder()
.update(EntityA)
.set({isActive: false})
.execute();
await queryRunner
.createQueryBuilder()
.update(EntityB)
.set({isActive: false})
.execute();
await queryRunner.commitTransaction();
}
catch (e) {
queryRunner.rollbackTransaction()
}
However, I suspect that this could all also be done using CASCADE and softDelete
I currently have the below query which is getting everything that has id = a certain value, but I need it to get only items that have a their column (called Uid) value appear more than once in the table.
Basically I need to filter out items with a unique Uid column value from the result of the below query
models.table.findAll({
where:{
id: req.params.id
}
})
So I believe the SQL query you're looking for would be
SELECT id, uid, count(*) FROM table
WHERE id = :id
GROUP BY id, uid
HAVING count(uid) > 1;
And in Sequelize it would be:
models.table.findAll({
where: { id: req.params.id },
group: ['id', 'uid'],
having: [sequelize.literal('count(?) > ?)', 'uid', 1]
})
Not entirely sure if that is the right syntax for Sequelize. Also look at sequelize.fn() and sequelize.col().
I have two table: "Users" which contain user data, and "Posts" which contain user's post. Each user can have many posts.
Users
id
full_name
Posts
post_no
post_content
id_user
What I want to do is hide users which has no post, so I tried to join the tables in my search model:
public function search($params) {
$query = Users::find->joinWith(['posts']);
}
but unfortunately, users with no post still shown.
Use mysql WHERE NOT NULL syntax
$query = Users::find->joinWith(['posts'])->where(['not', ['posts.post_no' => null]]);
WHERE NOT NULL alternative syntax for Yii2:
->where(['IS NOT', 'posts.post_no', null]);
->where(['<>', 'posts.post_no', null]);
->having(['posts.post_no' => 'NOT NULL']);
Please Check the official documentation here:
http://www.yiiframework.com/doc-2.0/yii-db-query.html#where()-detail
In my database I have two tables. Let's call them A and B. B table has foreign keys related to ids of records in A table.
I fetch couple of ids from A table and I want to find records in B table with foreign keys matching to those ids. However not every record in A table has records related to it in B table.
So in my code it looks like this:
var idsFromA = [1, 2, 3, 4, 5]
connection.query("SELECT * FROM B_table WHERE a_id = ?", idsFromA, function(err, results) {
if (err) {
return console.log(err)
} else {
// always empty
console.log(results);
}
});
Let's say ids number 1 and 2 from table A has record related to them in table B. Rest of them not. I want to get records from table B with those foreign keys but I receive only an empty array. It works only if I type query with only one matching value. But for the whole array of values where some of them don't have related record not.
How can I fix it and get information about those record?
Instead of = you have to use IN and you need to pass the array as value for the first placeholder.
connection.query("SELECT * FROM B_table WHERE a_id IN (?)", [idsFromA], ...)
The way you wrote it only the first id in the array idsFromA would be used for the ?.
Instead of using two queries you might want to use a LEFT JOIN, and the nestTables option.
connection.query({
query: 'SELECT * FROM A_table LEFT JOIN B_table ON (A_table.id=B_table.a_id) WHERE ...some condition to filter A...',
nestTables: true
}, function(err, results) {
if (err) {
console.log(err)
} else {
console.dir(results);
}
});
I have the following table strutucture and am accessing them by using MySQL Entity Framework:
Table Users
- Id
- Name
Table Subscriptions
- Id
- Id_User
- Id_Course
Table Courses
- Id
- Name
What I would like and am having a hard time to do so is building a link query for all users that returns a list with each entry containing:
User Id;
User name;
Concat string separated by comma with all courses for the user or 'no courses' string if none.
This list should be filtered by a part of users name.
I've started to build the code but can't finish it:
var Model db = new Model();
var list = from user in db.Users
join ???
where user.Name.Contains(filter.Trim())
select new { Name = user.Name, Id = user.Id, ???}
Can anyone help me please ?
You should use navigation properties (like User.Subscriptions) for this. Depending on how you created the model they may already be there, else you first should add them.
var query = from u in db.Users
where user.Name.Contains(filter) // trim the filter value first
select new
{
u.Name,
u.Id,
Courses = u.Subscriptions.Select(s => s.Course.Name)
};
var result = query.AsEnumerable()
.Select(q => new
{
q.Name,
q.Id
Courses = string.Join(", ", q.Courses)
};
The reason for doing this in two phases is that string.Join can't directly be used in an EF LINQ expression (can't be turned into SQL) so it must be done in memory (i.e. after an AsEnumerable).
But still it may be efficient to do a projection first (the first part), otherwise too much data may be fetched from the database.