質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.47%
Node.js

Node.jsとはGoogleのV8 JavaScriptエンジンを使用しているサーバーサイドのイベント駆動型プログラムです。

ORM

ORM(オブジェクト関係マッピング)はオブジェクト指向のシステムとリレーショナルデータベースの間でマッピングを行う技術です。

Express

ExpressはNode.jsのWebアプリケーションフレームワークです。 マルチページを構築するための機能セットおよびハイブリッドのWebアプリケーションを提供します。

Q&A

解決済

1回答

2650閲覧

sequelize で、 結合させた2テーブルのうち 片一方のテーブルにしか存在しないデータ を取得したい

aoisengaog

総合スコア4

Node.js

Node.jsとはGoogleのV8 JavaScriptエンジンを使用しているサーバーサイドのイベント駆動型プログラムです。

ORM

ORM(オブジェクト関係マッピング)はオブジェクト指向のシステムとリレーショナルデータベースの間でマッピングを行う技術です。

Express

ExpressはNode.jsのWebアプリケーションフレームワークです。 マルチページを構築するための機能セットおよびハイブリッドのWebアプリケーションを提供します。

0グッド

0クリップ

投稿2022/04/09 04:11

編集2022/04/09 10:42

タイトル通りとなりますが、結合させた状態で片一方にしか存在しないデータ取得を
LEFT JOIN と IS NULL でやろうと考えています。

環境:

"sequelize": "6.18.0", "sequelize-cli": "6.4.1", "sequelize-typescript": "2.1.3", "mariadb": "3.0.0", "typescript": "4.6.3",

studentsテーブルとteachersテーブルが多対多であり、2テーブルを中間テーブルa_students_teachersで紐づけています。

teachersと紐づかないstudentsを取得したいので、外部結合させてIS NULLでとってこれるものを取得したいのですが、うまくいっておらずアドバイス頂きたいです。

各テーブルのDDL

-- students CREATE TABLE `students` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `name` tinytext NOT NULL COMMENT '学生名', PRIMARY KEY (`id`), UNIQUE KEY `UNQ:students:uuid` (`uuid`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 -- teachers CREATE TABLE `teachers` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `name` tinytext NOT NULL COMMENT '教師名', PRIMARY KEY (`id`), ) ENGINE=InnoDB AUTO_INCREMENT=824 DEFAULT CHARSET=utf8mb4 -- a_students_teachers CREATE TABLE `a_students_teachers` ( `student_id` bigint(20) unsigned NOT NULL COMMENT '学生ID', `teacher_id` bigint(20) unsigned NOT NULL COMMENT '教師ID', PRIMARY KEY (`student_id`,`teacher_id`), KEY `IDX:a_students_teachers:student_id` (`student_id`), KEY `IDX:a_students_teachers:teacher_id` (`teacher_id`), CONSTRAINT `FK:teachers:a_students_teachers` FOREIGN KEY (`teacher_id`) REFERENCES `teachers` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT `FK:students:a_students_teachers` FOREIGN KEY (`student_id`) REFERENCES `students` (`id`) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4

SQLのイメージ(下記のSQLと同じことをSequelizeで行いたいです。)

(教師と紐付きがない、受け持ちが決まっていない生徒を出したいです)

select count(distinct students.id) from students left join a_students_teachers on students.id = a_students_teachers.student_id inner join teachers on teachers.id = a_students_teachers.teachers_id where a_students_teachers.teachers_id IS NULL

Repository(Sequelizeで実際に結果取得したいところはここに書いてます)

const count = await StudentModel.count({ distinct: true, col: 'id', where: {corportionId: null} // ここではうまくいかなかった。id にしたりincludeの内側に書いてみたりしたがうまくいっていない include: [ { required: false, model: teacherModel, } ] }); return count;

いい書き方ご存知の方、ご教示くださいm(- -)m

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

GitHubのリポジトリをOp.colで検索したら{ '$User.id_user$': { [Op.col]: 'Tasks.user_id' } },のような記述を見つけました。
'$table_name.column_name$'で結合先の列を指定する方法がわかりました。
これと[Op.is]: nullを組み合わせると実現できそうでした。

https://github.com/sequelize/sequelize/blob/ac89aac7fa2b40a61517e2de661dd849cc3c2ca4/test/unit/sql/generateJoin.test.js
https://sequelize.org/docs/v6/core-concepts/model-querying-basics/#operators

sqliteで試したコードも載せておきます。
countのSQLを発行する形にはなっていませんが、参考になりますでしょうか。

js

1const { Sequelize, DataTypes, Model, Op } = require('sequelize'); 2 3const main = async () => { 4 // Connecting to a database 5 // Option 2: Passing parameters separately (sqlite) 6 // https://sequelize.org/docs/v6/getting-started/#connecting-to-a-database 7 const sequelize = new Sequelize({ 8 dialect: 'sqlite', 9 storage: 'database.sqlite' 10 }); 11 12 // Extending Model 13 // https://sequelize.org/docs/v6/core-concepts/model-basics/#extending-model 14 class Student extends Model { } 15 Student.init({ 16 name: { 17 type: DataTypes.TEXT, 18 }, 19 }, { 20 sequelize, 21 modelName: 'students', 22 }) 23 class Teacher extends Model { } 24 Teacher.init({ 25 name: { 26 type: DataTypes.TEXT, 27 }, 28 }, { 29 sequelize, 30 modelName: 'teachers', 31 }) 32 class StudentTeacher extends Model { } 33 StudentTeacher.init({ 34 }, { 35 sequelize, 36 modelName: 'a_students_teachers', 37 }) 38 Student.belongsToMany(Teacher, { 39 through: StudentTeacher, 40 foreignKey: 'student_id', 41 }) 42 Teacher.belongsToMany(Student, { 43 through: StudentTeacher, 44 foreignKey: 'teacher_id', 45 }) 46 47 // Model synchronization 48 // https://sequelize.org/docs/v6/core-concepts/model-basics/#model-synchronization 49 await Student.sync({ force: true }) 50 await Teacher.sync({ force: true }) 51 await StudentTeacher.sync({ force: true }) 52 53 // Simple INSERT queries 54 // https://sequelize.org/docs/v6/core-concepts/model-querying-basics/#simple-insert-queries 55 const student1 = await Student.create({ name: '学生名1' }) 56 const student2 = await Student.create({ name: '学生名2' }) 57 const student3 = await Student.create({ name: '学生名3' }) 58 const student4 = await Student.create({ name: '学生名4' }) 59 const student5 = await Student.create({ name: '学生名5' }) 60 const teacher1 = await Teacher.create({ name: '教師名1' }) 61 const teacher2 = await Teacher.create({ name: '教師名2' }) 62 const teacher3 = await Teacher.create({ name: '教師名3' }) 63 const teacher4 = await Teacher.create({ name: '教師名4' }) 64 const teacher5 = await Teacher.create({ name: '教師名5' }) 65 66 // Special methods/mixins added to instances 67 // Foo.belongsToMany(Bar, { through: Baz }) 68 // https://sequelize.org/docs/v6/core-concepts/assocs/#foobelongstomanybar--through-baz- 69 await student1.addTeachers([teacher1, teacher2, teacher3]) 70 await student2.addTeachers([teacher2, teacher3, teacher4]) 71 await student4.addTeachers(teacher3) 72 73 // Advanced M:N Associations 74 // https://sequelize.org/docs/v6/advanced-association-concepts/advanced-many-to-many/#the-best-of-both-worlds-the-super-many-to-many-relationship 75 const students = await Student.findAll({ 76 include: [{ 77 model: Teacher, 78 }], 79 where: { 80 '$teachers.id$': { 81 [Op.is]: null, 82 }, 83 }, 84 }) 85 console.log(JSON.stringify(students)) 86 // [ 87 // { 88 // "id": 3, 89 // "name": "学生名3", 90 // "createdAt": "2022-04-09T12:45:46.518Z", 91 // "updatedAt": "2022-04-09T12:45:46.518Z", 92 // "teachers": [] 93 // }, 94 // { 95 // "id": 5, 96 // "name": "学生名5", 97 // "createdAt": "2022-04-09T12:45:46.519Z", 98 // "updatedAt": "2022-04-09T12:45:46.519Z", 99 // "teachers": [] 100 // } 101 // ] 102 console.log(students.length) 103 // 2 104} 105main()

投稿2022/04/09 13:55

退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

aoisengaog

2022/04/10 02:51

ご回答ありがとうございました! おかげさまで、やりたかった通りの条件で絞ることできました! > '$table_name.column_name$'で結合先の列を指定する方法がわかりました。 こんなやり方があったのですね! 半ば諦めて、findAllにしてSequelize.literalなど使って取得することを検討していましたので、とても助かりました! ``` attributes: [ [Sequelize.fn("count", Sequelize.literal('`student->teacher`.`id` IS NULL OR NULL')), "count"], ], ``` お手数ですが、もし余裕があれば追加質問させてください... 今後は自力でこの今回教えていただいたやり方に辿り着けるようになりたいのですが、 今回どのように調べてこの書き方を見つけられましたでしょうか? Githubで調べたときのことや、その他で調べたことやなにか簡単にで結構ですので、調べた簡単な手順やかかった時間など教えていただけませんでしょうか。
退会済みユーザー

退会済みユーザー

2022/04/10 04:38

コメントありがとうございます。 > 今回どのように調べてこの書き方を見つけられましたでしょうか? > Githubで調べたときのことや、その他で調べたことやなにか簡単にで結構ですので、調べた簡単な手順やかかった時間など教えていただけませんでしょうか。 今回、初めてSequelizeを知って、試してみましたので、 質問内容の問題解決に関する時間以外に、使い方を学ぶ時間も必要だったのですが、 package.jsonを作り始めたのが、18:35のようでした。 それから途中に食事をしたりお風呂に入ったりはしましたが、 22:55に回答の文章が書き終わった感じでした。 なので、3~4時間くらいかかったでしょうか。 (回答をしてみて、私自身、勉強になります) 調べたことは、公式のチュートリアルから始めました。 簡単なinsertとselectができるところまで進めてみて、ようやく質問内容のスタートラインに立てました・・ 最初、whereで結合先の列を指定する際に`teacher_id`とか`teacher.id`とか指定しても、 実際に発行されるSQLは`student.techer_id`とか`student.teacher.id`とかになってしまって、 Raw Queriesを使うしかないのかなとも思いました。 https://sequelize.org/docs/v6/core-concepts/model-querying-basics/#operators Model Querying - BasicsのOperatorsを見ていると、 `[Op.col]: 'user.organization_id'`のような記述があって、 これを使うと列の指定が正しくできるのではないかと思いました。 でも、これだけではどのような場合に使えるのかわかりませんでした。 (ドキュメントにはそれ以外に詳細な記述は見つけられませんでした・・) (強引にwhereに記述してみましたが、エラーになりました・・) `[Op.col]`の使い方を知りたかったので、GitHubのテストコードを探し始めました。 検索欄にOp.colと入力してリポジトリー内を検索しました。 すると、回答欄にも記載したテストコードが見つかりました。 https://github.com/sequelize/sequelize/blob/ac89aac7fa2b40a61517e2de661dd849cc3c2ca4/test/unit/sql/generateJoin.test.js [Op.col]の使い方を知りたかったのですが、見つけたテストコードを見て、 今回の質問内容の問題を解決するためには使えなそうだということがわかりました。 でも、発行されるSQLの列を直接指定しているような記述(`'$User.id_user$'`)がたまたま隣にありました。 これが使えそうと思いましたので、実際に試してみました。 (その後、これ(`'$User.id_user$'`)に関する内容について、ちょっとドキュメントを探してみたのですが、見つけられませんでした。もしかしたら使ってはいけない記述だったりするのかもしれません・・) こんな感じでした。 見つけられたのはたまたまでした・・ 見つけられなかったらもっと時間もかかっていたかもしれません・・
aoisengaog

2022/04/10 14:58

ご返答ありがとうございます! 時間かけて調べてくださったのですね...おかげさまで大変助かりました! それでも3,4時間でたどり着けたのはびっくりしました... 下地がかなりあることが前提だとは思いますが、 同じ手順で辿り着けるように、わからないことはチュートリアルでも、最初から諦めず調べてみるようにします! >`[Op.col]`の使い方を知りたかったので、GitHubのテストコードを探し始めました。 また、特にこちらが今回自分で調べた時ともっとも違うところだと感じました。 私の場合はまさに同じ状況になって、単に[Op.col]でぐぐるだけ、などをしてしまってたのですが、 公式のGithubのテストコードを調べるやり方は思いつかなかったので、積極的に使ってみるようにします 改めて、今回はどうもありがとうございました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.47%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問