@adamscybot ์ด tgriesser/bookshelf#55์์ ๊ฐ์ ธ์จ ๊ฒ - ์ด๊ฒ์ ์ถ๊ฐํ๊ธฐ์ ์ข์ ๊ธฐ๋ฅ์ผ ์ ์์ต๋๋ค.
๋์ํฉ๋๋ค. ์ด ๊ธฐ๋ฅ์ _์ข์ ๊ฒ์ ๋๋ค_!
:+1:
:+1:
CSV์์ ์ผ๋ถ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๊ณ ์๋๋ฐ ๋ง์ง๋ง ๊ฐ์ ธ์ค๊ธฐ์์ ์ผ๋ถ ๋ ์ฝ๋๊ฐ ๊ฒน์น ๊ฐ๋ฅ์ฑ์ด ๋์ต๋๋ค(์: ๋ง์ง๋ง์ผ๋ก ๊ฐ์ ธ์จ ๊ฒ์ 1์ 1์ผ๋ถํฐ 5์ 31์ผ๊น์ง, ์ด๋ฒ์๋ 5์ 31์ผ๋ถํฐ 6์ 18์ผ๊น์ง ๊ฐ์ ธ์ด).
๋คํํ ํ์ฌ ์์คํ ์ ์์ ์ ์ผ๋ก ๊ณ ์ ํ ID๋ฅผ ํ ๋นํฉ๋๋ค.
์ ๋ ์ฝ๋๋ฅผ ์ฝ์ ํ๊ณ ์ด์ ๋ ์ฝ๋๋ฅผ ์ ๋ฐ์ดํธํ๋ ๊ฐ์ฅ ์ข์ ๋ฐฉ๋ฒ์ ๋ฌด์์ ๋๊น?
๋๋ ์์ง ๊ทธ๊ฒ์ ์๋ํ์ง ์์์ง๋ง ๋ค์๊ณผ ๊ฐ์ ๊ฒ์ด๋ผ๊ณ ์๊ฐํ์ต๋๋ค.
var ids = records.map(function (json) { return json.id })
;
Records.forge(ids).fetchAll().then(function () {
records.forEach(function (record) {
// now the existing records are loaded in the collection ?
Object.keys(record).forEach(function (key) {
Records.forge(record.id).set(key, record[key]);
});
});
Records.invokeThen('save').then(function () {
console.log('Records have been either inserted or updated');
});
});
๋ํ ๋๋ก๋ ๋ด๊ฐ ์ ์ฅํ๊ณ ์๋ ๊ฒ์ด ํด์์ ๊ฐ์ ํ์ id ๊ฐ์ผ๋ก ์ ์ฅ๋ฉ๋๋ค. ์ด ๊ฒฝ์ฐ ๋ฐ์ดํฐ๋ฅผ ์ถ๊ฐํ๊ฑฐ๋ ๊ต์ฒดํ๊ธฐ๋ง ํ๋ฉด ๋ฉ๋๋ค.
๋๋ ํญ์ SQL์ ์ ํต์ ์ธ SQL๋ก ์ฌ์ฉํ์ง ์์ต๋๋ค. ์ข ์ข ๋ช ํํ ๊ด๊ณ ๋งคํ ๋ฐ ์ธ๋ฑ์ค์ ์ด์ ๊ณผ ํจ๊ป ํ์ด๋ธ๋ฆฌ๋ NoSQL๋ก ์ฌ์ฉํฉ๋๋ค.
:+1:
์๋
,
์ด ์๋ก์ด ๊ธฐ๋ฅ์ ๋ํ ์์์ด ์์ต๋๊น?
์๋๋ฉด ๋๊ตฐ๊ฐ mysql์ ๋ํด ์ด ๊ธฐ๋ฅ์ ์๋ฎฌ๋ ์ด์ ํ๋ ๋ฐฉ๋ฒ์ ๋ณด์ฌ์ฃผ๋ ์์ ๋ฅผ ์ถ์ฒํ ์ ์์ต๋๊น?
๊ณ ๋ง์
์ง๊ธ์ raw
๋ฅผ ์ฌ์ฉํ๊ณ ์์ง๋ง ๊ณง ์ฌ๊ธฐ์์ ์ฌ์ฉํ ์ ์๋๋ก ์ด์ฌํ ๋
ธ๋ ฅํ๊ณ ์์ต๋๋ค.
Postgres๋ ๋ฐฉ๊ธ upsert ์ง์์ ๊ตฌํํ์ต๋๋ค. +1:
https://news.ycombinator.com/item?id=9509870
๊ตฌ๋ฌธ์ INSERT ... ON CONFLICT DO UPDATE
์
๋๋ค.
MySql์์ REPLACE INTO
๋ฅผ ์ํํ๋ ๋ฐฉ๋ฒ์ ์ฐพ๊ณ ์์๋๋ฐ ์ด ๊ธฐ๋ฅ ์์ฒญ์ ์ฐพ์์ต๋๋ค. REPLACE
๋ฐ INSERT
๋ MySql์์ ์ ํํ ๋์ผํ ๊ตฌ๋ฌธ์ ๊ฐ์ง๊ณ ์๊ธฐ ๋๋ฌธ์ ON DUPLICATE KEY UPDATE
๋ณด๋ค ๊ตฌํํ๊ธฐ๊ฐ ๋ ์ฝ๋ค๊ณ ์์ํ ์ ์์ต๋๋ค. REPLACE
๋ฅผ ๊ตฌํํ ๊ณํ์ด ์์ต๋๊น? PR์ด ๊ฐ์น ์๋ ๊ฒ์
๋๊น?
ํนํ PostreSQL 9.5์์ ์ด์ ๋ํ ์ ๋ฐ์ดํธ๊ฐ ์์ต๋๊น?
ํ ๊ฐ์ง ์ค์ํ ์ง๋ฌธ์ PostgreSQL ๋ฐ MySQL๊ณผ ๊ฐ์ ๋ค๋ฅธ ๋ฐฉ์ธ์ ๋ํด ๋์ผํ upsert
๋ฉ์๋ ์๋ช
์ ๋
ธ์ถํ ์ง ์ฌ๋ถ์
๋๋ค. Sequelize์์ upsert
์ ๋ฐํ ๊ฐ๊ณผ ๊ด๋ จํ์ฌ ๋ฌธ์ ๊ฐ ๋ฐ์ํ์ต๋๋ค: https://github.com/sequelize/sequelize/issues/3354.
์ผ๋ถ KnexJS ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ฉ์๋๋ ๋ค๋ฅธ ๋ฐฉ์ธ์ ์ปจํ
์คํธ์์ ๋ฐํ ๊ฐ๊ณผ ๊ด๋ จํ์ฌ ๊ตฌ๋ณ์ด ์์์ ์๊ณ ์์ต๋๋ค(์: insert
, ์ฌ๊ธฐ์ ์ฒซ ๋ฒ์งธ ์ฝ์
๋ ID์ ๋ฐฐ์ด์ด Sqlite ๋ฐ MySQL์ ๋ํด ๋ฐํ๋๋ ๋ฐ๋ฉด ๋ชจ๋ ๋ฐฐ์ด์ ์ฝ์
๋ ID๋ PostgreSQL๊ณผ ํจ๊ป ๋ฐํ๋ฉ๋๋ค).
์ค๋ช
์์ ๋ฐ๋ฅด๋ฉด MySQL์ INSERT ... ON DUPLICATE KEY UPDATE
๊ตฌ๋ฌธ์๋ ๋ค์๊ณผ ๊ฐ์ ๋์์ด ์์ต๋๋ค(http://dev.mysql.com/doc/refman/5.7/en/insert-on-duplicate.html).
ON DUPLICATE KEY UPDATE๋ฅผ ์ฌ์ฉํ๋ฉด ํ๋น ์ํฅ์ ๋ฐ๋ ํ ๊ฐ์ ํ์ด ์ ํ์ผ๋ก ์ฝ์ ๋๋ฉด 1, ๊ธฐ์กด ํ์ด ์ ๋ฐ์ดํธ๋๋ฉด 2, ๊ธฐ์กด ํ์ด ํ์ฌ ๊ฐ์ผ๋ก ์ค์ ๋ ๊ฒฝ์ฐ 0์ ๋๋ค.
PostgreSQL์์ (http://www.postgresql.org/docs/9.5/static/sql-insert.html):
์ฑ๊ณต์ ์ผ๋ก ์๋ฃ๋๋ฉด INSERT ๋ช ๋ น์ ๋ค์ ํ์์ ๋ช ๋ น ํ๊ทธ๋ฅผ ๋ฐํํฉ๋๋ค.
INSERT oid count
count๋ ์ฝ์ ๋๊ฑฐ๋ ์ ๋ฐ์ดํธ๋ ํ์ ์์ ๋๋ค. count๊ฐ ์ ํํ 1์ด๊ณ ๋์ ํ ์ด๋ธ์ OID๊ฐ ์๋ ๊ฒฝ์ฐ oid๋ ์ฝ์ ๋ ํ์ ํ ๋น๋ OID์ ๋๋ค. ๋จ์ผ ํ์ด ์ ๋ฐ์ดํธ๋์ง ์๊ณ ์ฝ์ ๋์ด์ผ ํฉ๋๋ค. ๊ทธ๋ ์ง ์์ผ๋ฉด oid๋ 0์ ๋๋ค.
INSERT ๋ช ๋ น์ RETURNING ์ ์ด ํฌํจ๋ ๊ฒฝ์ฐ ๊ฒฐ๊ณผ๋ ๋ช ๋ น์ ์ํด ์ฝ์ ๋๊ฑฐ๋ ์ ๋ฐ์ดํธ๋ ํ์ ๋ํด ๊ณ์ฐ๋ RETURNING ๋ชฉ๋ก์ ์ ์๋ ์ด๊ณผ ๊ฐ์ ํฌํจํ๋ SELECT ๋ฌธ์ ๊ฒฐ๊ณผ์ ์ ์ฌํฉ๋๋ค.
์ด ๊ฒฝ์ฐ RETURNING
์ ๋ก ๋ฐํ๊ฐ์ ๋ณ๊ฒฝํ ์ ์์ต๋๋ค.
์๊ฐ?
๋๋ ์ฝ์ ์ ์ํ "onConflict" ๋ฉ์๋๋ฅผ ์ถ๊ฐํ๊ธฐ ์ํด Client_PG๋ฅผ ์์ญ์ด ํจ์นํ๋ค. github oauth ์๊ฒฉ ์ฆ๋ช ์ upsertํ๋ ค๋ ๊ฒฝ์ฐ ๋ค์๊ณผ ๊ฐ์ด ์ฟผ๋ฆฌ๋ฅผ ์์ฑํ ์ ์์ต๋๋ค.
const profile = {
access_token: "blah blah",
username: "foobar",
// ... etc
}
const oauth = {
uid: "13344398",
provider: "github",
created_at: new Date(),
updated_at: new Date(),
info: profile,
};
// todo: add a "timestamp" method
const insert = knex("oauths").insert(oauth).onConflict(["provider", "uid"],{
info: profile,
updated_at: new Date(),
});
console.log(insert.toString())
์ด ์ด๋ฆ์ ๋ฐฐ์ด์ ๊ณ ์ ์ฑ ์ ์ฝ ์กฐ๊ฑด์ ์ง์ ํฉ๋๋ค.
insert into "authentications" ("created_at", "info", "provider", "uid", "updated_at") values ('2016-02-14T14:42:18.342+08:00', '{\"access_token\":\"blah blah\",\"username\":\"foobar\"}', 'github', '13344398', '2016-02-14T14:42:18.342+08:00') on conflict ("provider", "uid") do update set "info" = '{\"access_token\":\"blah blah\",\"username\":\"foobar\"}', "updated_at" = '2016-02-14T14:42:18.343+08:00'
์์ญ์ด ํจ์น์ ๋ํ ์์ : https://gist.github.com/hayeah/1c8d642df5cfeabc2a5b ๋ฅผ ์ฐธ์กฐํ์ธ์.
์ด๊ฒ์ ๋งค์ฐ ํดํน๋ ์คํ์ ๋๋ค... ๋ฐ๋ผ์ ์์ญ์ด ํจ์น๋ฅผ ํ๋ก๋์ ์ฝ๋์ ์ ํํ ๋ณต์ฌํ์ฌ ๋ถ์ฌ๋ฃ์ง ๋ง์ญ์์ค. p
์๋ ค์ง ๋ฌธ์ :
count = count + 1
์ ๊ฐ์ ์์ ์
๋ฐ์ดํธ๋ฅผ ์ง์ํ์ง ์์ต๋๋ค.ํผ๋๋ฐฑ?
@hayeah ๋๋ ๋น์ ์ ์ ๊ทผ ๋ฐฉ์์ ์ข์ํ๊ณ Postgres์ ์ ํฉํฉ๋๋ค. ๋๋ ๋น์ ์ด ์ง์ ํ ๊ฒ ์ด์ธ์ ๋ค๋ฅธ ๋ฌธ์ ๋ฅผ ๊ฒฝํ์ ์ผ๋ก ๊ฐ์งํ ์ ์๋์ง ํ์ธํ๊ธฐ ์ํด ํ๋ก์ ํธ์์ ์์ญ์ด ํจ์น๋ฅผ ์๋ํ ๊ฒ์ ๋๋ค.
๊ตฌ๋ฌธ ์ ์: knex('table').upsert(['col1','col2']).insert({...}).update({...});
์ฌ๊ธฐ์ upsert
๋ ์กฐ๊ฑด๋ฌธ์ ์ฌ์ฉํฉ๋๋ค. ์ด ๋ฐฉ๋ฒ์ db์ ๊ตญํ๋์ง ์์ต๋๋ค.
upsert์ ๋ค์ํ ๊ตฌํ์ ๋ํ ์์ฝ์ https://en.wikipedia.org/wiki/Merge_(SQL )์์ ์ฐพ์ ์ ์์ต๋๋ค.
์ ๋ ์ด ๋ฅ๋ ฅ์ ๊ด์ฌ์ด ์์ต๋๋ค. ์ฌ์ฉ ์ฌ๋ก: ์ธ๋ถ ์๋น์ค์ ๋ง์ ์ธ๋ถ ๋ฐ์ดํฐ์ ์์กดํ๋ ์์คํ ๊ตฌ์ถ ๋ก์ปฌ MySQL db์ ์ ์ฅํ๋ ๋ฐ์ดํฐ์ ๋ํด ์ฃผ๊ธฐ์ ์ผ๋ก ํด๋งํฉ๋๋ค. ์๋ง๋ ์ง๊ธ์ knex.raw๋ฅผ ์ฌ์ฉํ ๊ฒ์ ๋๋ค.
๋ํ ๊ด์ฌ์ด ์์ง๋ง ๋ด ์ฌ์ฉ ์ฌ๋ก์์๋ ์ด์ ํญ์ '๊ณ ์ ' ์ ์ฝ ์กฐ๊ฑด์ด ์๋ ๊ฒ์ ์๋๋ฏ๋ก ์ถฉ๋์ ๊ธฐ๋ฐ์ผ๋ก ํ์ง ์๋ ๋ฐฉ์์ผ๋ก ์๋ํด์ผ ํฉ๋๋ค. ์กด์ฌํ๋ ๊ฒฝ์ฐ ์ฟผ๋ฆฌ์ ์ผ์นํ๋ ํญ๋ชฉ์ ์ ๋ฐ์ดํธํ๊ธฐ๋ง ํ๋ฉด ๋ฉ๋๋ค. ๊ทธ๋ ์ง ์์ผ๋ฉด ์ฝ์ ์ ํ.
@haywirez ๊ณ ์ ์ ์ฝ ์กฐ๊ฑด์ด ์๋ ์ด์ ๊ฐ ๊ถ๊ธํฉ๋๋ค. ๋น์ ์ ๊ฒฝ์ ์กฐ๊ฑด์ ๋ ธ์ถ๋์ง ์์๊น์?
@hayeah ๋๋ ์ฃผ์ด์ง ๋ ์ง์ ์ฐ๊ฒฐ๋ ๊ฐ์ด ์๋ ํญ๋ชฉ์ ์ ์ฅํ๋ ์๊ฐ ์ฐฝ ๋ฐ์ดํฐ์ ๋ํ ํน์ ์ฌ์ฉ ์ฌ๋ก๊ฐ ์์ต๋๋ค. ๋ฐ๋ผ์ ์ผ์นํ๋ (์ผ) ํ์์คํฌํ์ "๊ฒฐํฉ๋ ํค"์ ๋ค๋ฅธ ํ ์ด๋ธ์ PK์ ํด๋นํ๋ ๋ ๊ฐ์ ๋ค๋ฅธ ID๊ฐ ์๋ ํญ๋ชฉ์ ์ฝ์ ํ๊ณ ์ ๋ฐ์ดํธํฉ๋๋ค. 24์๊ฐ ์ด๋ด์ ์ฝ์ ํ๊ฑฐ๋ ์ต์ ์นด์ดํธ๋ก ์ ๋ฐ์ดํธํด์ผ ํฉ๋๋ค.
์ด๊ฒ์ ์ข์ ๊ธฐ๋ฅ์ด ๋ ๊ฒ์ ๋๋ค!
์ฌ๊ธฐ์ ๋๊ธ์ ๋ฌ์์ฃผ์ ๋ชจ๋ ๋ถ๋ค ์๋ ํ์ธ์. PRPlease ๋ ์ด๋ธ์ ์ถ๊ฐํ๊ณ ์์ต๋๋ค.
์ด ๊ธฐ๋ฅ์ ์ถ๊ฐํ๋ PR์ ํ๊ฒ ๋์ด ๊ธฐ์ฉ๋๋ค. ํ์ง๋ง ๋จผ์ ์ฌ๊ธฐ์์ ์ํ๋ API์ ๋ํ ํ ๋ก ์ ๋ณด๊ณ ์ถ์ต๋๋ค.
์ถ์ .
^^ ๋์ํฉ๋๋ค.
๋๋ ์ด์ ๊ฐ์ ๋๊ธ์ ์ญ์ ํ ๊ฒ์ ๋๋ค. +1์ ์ถ๊ฐํ๋ ค๋ฉด ์์ ์ด๋ชจํฐ์ฝ ๋ฐ์์ผ๋ก ๊ทธ๋ ๊ฒ ํ์ธ์.
@willfarrell ๋ฐ @hayeah ์ ์์์์ ๊ฐ์ด ์ด ์ ํ ๋ฐฐ์ด์ ์ฝ๊ฐ์ ๋ฌธ์ ๊ฐ ์์ต๋๋ค. ์ด๋ฌํ ์๊ฐ json
์์ฑ์ ์ง์ํ ์ ์๋์ง ํ์คํ์ง ์์ต๋๋ค. ์ด๋ฌํ ์ ์ ์ค ์ด๋ ๊ฒ๋ ๋ ์ฝ๋์ ์ผ์นํ๋ where ๋ฌธ/์ ์ ํ "์ฟผ๋ฆฌ"๋ฅผ ํฌํจํ์ง ์๋ ์ด์ ๊ฐ ์์ต๋๊น?
์ ์ 1
knex('table')
.where('id', '=', data.id)
.upsert(data)
์ ์ 2
knex('table')
.upsertQuery(knex => {
return knex('table')
.where('id', '=', data.id)
})
.upsertUpdate(knex => {
return knex('table')
.insert(data)
})
์ ์ 3
knex('table')
.where('id', '=', data.id)
.insert(data)
.upsert() // or .onConflictDoUpdate()
๋๋ 3๊ณผ ๊ฐ์ ๊ฒ์ ๊ฐ์ฅ ๋ง์ด ๊ธฐ์ธ๊ณ ์๋ค.
์ฌ๊ธฐ์ mongodb๊ฐ ์ํํ๋ ๋ฐฉ๋ฒ์ ์ถ๊ฐํ๊ธฐ๋ง ํ๋ฉด ๋ฉ๋๋ค .
db.collection.update(
<query>,
<update>,
{
upsert: <boolean>,
multi: <boolean>,
writeConcern: <document>
}
)
@reggi ๋ด ์์ญ์ด ํจ์น๊ฐ where
์ ํธํ๋๋ค๊ณ ์๊ฐํฉ๋๋ค ...
@reggi ๋๋ ๋น์ ์ ์์ ์ ์ดํดํ์ง ๋ชปํฉ๋๋ค.
@willfarrell ๋ฐ @hayeah ์ ์์์ ์ ์๋ ์ ๊ทผ ๋ฐฉ์์์ ์ด๋ค ๊ธฐ๋ฅ์ด ๋๋ฝ๋์๋์ง ์์ธํ ์ค๋ช
ํ ์ ์์ต๋๊น?
์ where
์ด ํ์ํฉ๋๊น?
insert
์์
์ผ ๋ฟ์
๋๋ค.
@reggi ์ ๊ณตํ MongoDB ์์ ๋ "๋จผ์ UPDATE WHERE ์๋ ... ์ฟผ๋ฆฌ์ ์ผ์นํ๋ ๋ฌธ์๊ฐ ์์ผ๋ฉด INSERT ์ํ"์ ์ฝ๋ ๋ฐ๋ฉด SQL UPSERT๋ "INSERT INTO ... ์ด ๊ธฐ๋ณธ ํค๊ฐ ์๋ ํ์ด ์ด๋ฏธ ์๋ ๊ฒฝ์ฐ UPDATING"์ด๋ผ๊ณ ์ฝ์ต๋๋ค. .
๋ฐ๋ผ์ SQL ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ๊ตฌํ๋๋ ๊ฒ๊ณผ๋ ์์ ํ ๋ค๋ฅธ "์
์ํธ"์ ๋ํด ์ด์ผ๊ธฐํ๊ณ ์๋ ๊ฒ ๊ฐ์ต๋๋ค.
๋๋ ์ด API๋ฅผ ์ ์ํ ๊ฒ์ด๋ค:
knex.createTable('test')
.bigserial('id')
.varchar('unique').notNull().unique()
.varchar('whatever')
knex.table('test').insert(object, { upsert: ['unique'] })
.insert()
ํจ์๋ ๋ ๋ฒ์งธ ๋งค๊ฐ๋ณ์๋ฅผ ๋ถ์ํฉ๋๋ค.
๋ฌธ์์ด์ด๋ฉด ์ด์ returning
๋งค๊ฐ๋ณ์์
๋๋ค.
๊ฐ์ฒด์ธ ๊ฒฝ์ฐ options.returning
๋ฐ options.upsert
๊ฐ ์๋ options
๋งค๊ฐ๋ณ์์
๋๋ค. ์ฌ๊ธฐ์ options.upsert
๋ ๊ณ ์ ํค ๋ชฉ๋ก์
๋๋ค. ๋ณตํฉ ๊ณ ์ ํค ์ ์ฝ ์กฐ๊ฑด์ ๊ฒฝ์ฐ).
๊ทธ๋ฐ ๋ค์ object
์์ ๊ธฐ๋ณธ ํค์ ๋ชจ๋ options.upsert
ํค๋ฅผ ์ ์ธํ๊ณ ( clone(object) && delete cloned_object.id && delete cloned_object.unique
๋ฅผ ํตํด) ์ ๊ฑฐ๋ cloned_object
๋ฅผ ์ฌ์ฉํ๋ SQL ์ฟผ๋ฆฌ๊ฐ ์์ฑ๋ฉ๋๋ค. SQL ์ฟผ๋ฆฌ์ ๋ ๋ฒ์งธ ๋ถ๋ถ์์ SET
์ ์ ๊ตฌ์ฑํ๊ธฐ ์ํ ๊ธฐ๋ณธ(๋ฐ ๊ณ ์ ) ํค: ... ON CONFLICT DO UPDATE SET [iterate cloned_object]
.
๊ทธ๊ฒ์ด ํ์ฌ API์ ๋์ง์ ์ธ ๊ฐ์ฅ ๊ฐ๋จํ๊ณ ๋ชจํธํ์ง ์์ ์๋ฃจ์ ์ด ๋ ๊ฒ์ด๋ผ๊ณ ์๊ฐํฉ๋๋ค.
@slavafomin @ScionOfBytes API๋ ์์ง ํฉ์๋์ง ์์ ๊ฒ ๊ฐ์ต๋๋ค. ๊ทธ๊ฒ์ด ๋ค์ ๋จ๊ณ๊ฐ ๋ ๊ฒ์ด๊ณ ๊ทธ๊ฒ์ ๊ตฌํํ๊ธฐ๋ฅผ ์ข์ํ๋ ์ฌ๋์ ๊ทธ๋ ๊ฒ ํ ์ ์์ต๋๋ค. ๊ทธ๋์ ์์์ด ์์ต๋๋ค.
์ถ์ . ์ด ์ค๋ ๋๊ฐ ๋ด์ค ์์ฒญ ์คํธ ๋ฐ ๊ธฐํ ๊ด๋ จ์ฑ์ด ๋ํ ๋ฉ์์ง๋ก ์ฑ์์ง๋ ๊ฒ์ ๋ฐฉ์งํ๊ธฐ ์ํ ์ถ๊ฐ ๋ด์ค ์์ฒญ์ด ์๋ ๊ฒฝ์ฐ ๋ด์ค์ ๋ํ ์ถ๊ฐ ์์ฒญ์ ์ญ์ ํ๊ธฐ ์์ํ์ต๋๋ค.
@amir-s ๋์ํ์ง๋ง ์ด ๋ฌธ์ ์ ์ฃผ์ ๋ upsert ๊ธฐ๋ฅ์ ๋๋ค.
IMO, ์ง์ง ๋ฌธ์ ๋ API๊ฐ ์๋๋ผ ๊ฐ ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ upsert๋ฅผ ์ํํ๋ ๋๋ฌธ ๋ฐฉ๋ฒ์ ๋๋ค.
MySQL(ON DUPLICATE KEY UPDATE) ๋ฐ PostgreSQL 9.5+(ON CONFLICT DO UPDATE)๋ ๊ธฐ๋ณธ์ ์ผ๋ก upsert๋ฅผ ์ง์ํฉ๋๋ค.
MSSQL๊ณผ Oracle์ merge ์ ๋ก ์ด๋ฅผ ์ง์ํ ์ ์์ง๋ง, knex๊ฐ ์ฟผ๋ฆฌ๋ฅผ ๊ตฌ์ฑํ ์ ์์ผ๋ ค๋ฉด ์ถฉ๋ํ๋ ์ด์ ์ด๋ฆ์ ์์์ผ ํฉ๋๋ค.
-- in this case the conflict column is 'a'
merge into target
using (values (?)) as t(a)
on (t.a = target.a)
when matched then
update set b = ?
when not matched then
insert (a, b) values (?, ?);
๊ทธ๋ฌ๋ SQLite๋ ๊ทธ๋ ์ง ์์์ต๋๋ค. upsert๋ฅผ ์๋ฎฌ๋ ์ด์ ํ๋ ค๋ฉด ๋ ๊ฐ์ ์ฟผ๋ฆฌ๊ฐ ํ์ํฉ๋๋ค.
-- 'a' is the conflict column
insert or ignore into target (a, b) values (?, ?);
update target set b = ?2 where changes() = 0 and a = ?1;
๋๋ INSERT OR REPLACE
, REPLACE
์ฌ์ฉ
-- replace will delete the matched row then add a new one with the given data
replace into target (a, b) values (?, ?);
๋ถํํ๋ ๋์ ํ ์ด๋ธ์ ๋ฐ b๋ณด๋ค ๋ ๋ง์ ์ด์ด ์๋ ๊ฒฝ์ฐ ํด๋น ๊ฐ์ ๊ธฐ๋ณธ์ ์ผ๋ก ๋์ฒด๋ฉ๋๋ค.
insert or replace into target (a, b, c) values (?, ?, (select c from target where a = ?1))
CTE๋ฅผ ์ฌ์ฉํ๋ ๋ ๋ค๋ฅธ ์๋ฃจ์ ์ ์ด stackoverflow ๋ต๋ณ ์ ์ฐธ์กฐํ์ธ์.
๋๋ knex ๊ธฐ๋ฐ Postgres upsert๋ฅผ ์ฐพ์ ์ด ๋ฌธ์ ์ ์ฌ๋ฌ ๋ฒ ์์ต๋๋ค. ๋ค๋ฅธ ์ฌ๋์ด ์ด๊ฒ์ ํ์๋ก ํ๋ ๊ฒฝ์ฐ ๋ฐฉ๋ฒ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค. ๋จ์ผ ๋ฐ ๋ณตํฉ ๊ณ ์ ํค์ ๋ํด ์ด๊ฒ์ ํ ์คํธํ์ต๋๋ค.
์๋๋ฅผ ์ฌ์ฉํ์ฌ ํ ์ด๋ธ์ ๊ณ ์ ํค ์ ์ฝ ์กฐ๊ฑด์ ๋ง๋ญ๋๋ค. ๋ณตํฉ ํค ์ ์ฝ ์กฐ๊ฑด์ด ํ์ํ์ต๋๋ค.
table.unique(['a', 'b'])
(ํธ์ง: ์์ ๋งค๊ฐ๋ณ์ ๋ฐ์ธ๋ฉ์ ์ฌ์ฉํ๋๋ก ์ ๋ฐ์ดํธ๋จ)
const upsert = (params)=> {
const {table, object, constraint} = params;
const insert = knex(table).insert(object);
const update = knex.queryBuilder().update(object);
return knex.raw(`? ON CONFLICT ${constraint} DO ? returning *`, [insert, update]).get('rows').get(0);
};
const objToUpsert = {a:1, b:2, c:3}
upsert({
table: 'test',
object: objToUpsert,
constraint: '(a, b)',
})
์ ์ฝ ์กฐ๊ฑด์ด ํฉ์ฑ์ด ์๋ ๊ฒฝ์ฐ ๋น์ฐํ ํ ์ค์ constraint: '(a)'
์
๋๋ค.
์ ๋ฐ์ดํธ๋ ๊ฐ์ฒด ๋๋ ์ฝ์ ๋ ๊ฐ์ฒด๋ฅผ ๋ฐํํฉ๋๋ค.
๋ณตํฉ nullable ์ธ๋ฑ์ค์ ๋ํ ์ฐธ๊ณ ์ฌํญ
๋ณตํฉ ์ธ๋ฑ์ค
(a,b)
๊ฐ ์๊ณb
๊ฐ nullable์ด๋ฉด(1, NULL)
๋ฐ(1, NULL)
๊ฐ์ Postgres์์ ์ํธ ๊ณ ์ ํ ๊ฒ์ผ๋ก ๊ฐ์ฃผ๋ฉ๋๋ค(์ดํดํ ์ ์์ต๋๋ค. ์ด๋ ํ๋). ์ด๊ฒ์ด ์ฌ์ฉ ์ฌ๋ก์ธ ๊ฒฝ์ฐ ๋ถ๋ถ ๊ณ ์ ์ธ๋ฑ์ค๋ฅผ ๋ง๋ ๋ค์ upsert ์ ์ null์ ํ ์คํธํ์ฌ ์ฌ์ฉํ ์ ์ฝ ์กฐ๊ฑด์ ๊ฒฐ์ ํด์ผ ํฉ๋๋ค. ๋ถ๋ถ ๊ณ ์ ์ธ๋ฑ์ค๋ฅผ ๋ง๋๋ ๋ฐฉ๋ฒ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.CREATE UNIQUE INDEX unique_index_name ON table (a) WHERE b IS NULL
. ํ ์คํธ์์b
๊ฐ null์ธ ๊ฒ์ผ๋ก ํ์ธ๋๋ฉด upsert์์constraint: '(a) WHERE b IS NULL'
์ ์ฝ ์กฐ๊ฑด์ ์ฌ์ฉํด์ผ ํฉ๋๋ค.a
๋ nullable์ธ ๊ฒฝ์ฐ 3๊ฐ์ ๊ณ ์ ์ธ๋ฑ์ค์ 4๊ฐ์if/else
๋ถ๊ธฐ๊ฐ ํ์ํ๋ค๊ณ ์๊ฐํฉ๋๋ค(๋ด ์ฌ์ฉ ์ฌ๋ก๊ฐ ์๋๋ฏ๋ก ํ์คํ์ง ์์).
๋ค์์ ์ปดํ์ผ๋ ์๋ฐ์คํฌ๋ฆฝํธ ์ ๋๋ค.
๋๊ตฐ๊ฐ๊ฐ ์ด๊ฒ์ ์ ์ฉํ๊ฒ ์ฌ์ฉํ๊ธฐ๋ฅผ ๋ฐ๋๋๋ค. @elhigu (ํธ์ง: Nevermind - ๊ฒฝ๊ณ ๋ฅผ ๋ณด์์ต๋๋ค - ์ง๊ธ knex().update(object)
์ฌ์ฉ์ ๋ํ ์๊ฒฌ์ด ์์ผ์ญ๋๊น?knex.queryBuilder()
์ฌ์ฉ)
@timhuff ๋ ๋ฉ์ง๊ฒ ๋ณด์
๋๋ค. ๋ณ๊ฒฝํด์ผ ํ ํ ๊ฐ์ง๋ ๊ฐ ๋ฐ์ธ๋ฉ์ ์ฌ์ฉํ์ฌ ๊ฐ ์ฟผ๋ฆฌ๋ฅผ ์์์ ์ ๋ฌํ๋ ๊ฒ์
๋๋ค. ๊ทธ๋ ์ง ์์ผ๋ฉด query.toString()
๊ฐ ์ฟผ๋ฆฌ์ ๊ฐ ๋ถ๋ถ์ ๋ ๋๋งํ๋ ๋ฐ ์ฌ์ฉ๋๋ฉฐ ๊ฐ๋ฅํ ์ข
์์ฑ ์ฃผ์
๊ตฌ๋ฉ์ด ์ด๋ฆฝ๋๋ค(queryBuilder.toString()๋ ๋งค๊ฐ๋ณ์๋ฅผ ๋ฐ์ธ๋ฉ๋งํผ ๋๋ผ์ด๋ฒ์ ์ ๋ฌํ๋ ๊ฒ๋งํผ ์์ ํ์ง ์์ต๋๋ค).
@elhigu ์ ๊น... query.toString()
๋ ๋ฐ์ธ๋ฉ์ ์ฌ์ฉํ์ง ์์ต๋๊น? ๊ถ์ฅํ๊ณ ์๋ ์์ ์ฌํญ์ ๋๋ต์ ์ธ ์๋ฅผ ๋ค์ด ์ฃผ์๊ฒ ์ต๋๊น? ๋๋... ์
๋ฐ์ดํธํ ์ฝ๋๊ฐ ๋ง์ ์ ์์ต๋๋ค.
Raw bindings ๋ผ๋ ๋ ์ด๋ธ์ด ๋ถ์ ๋ฌธ์ ๋ถ๋ถ์ ์ฐพ์์ต๋๋ค. ์ง๊ธ ์
๋ฐ์ดํธํ๊ธฐ ์์ ๋ฅผ ์
๋ฐ์ดํธํ์ต๋๋ค. query.toString
๋ ์์ ํ๋ค๊ณ ์๊ฐํ์ต๋๋ค. ๋ฌธ์ ์น์
์ "์์ ํ์ง ์์ ์ฟผ๋ฆฌ๋ฅผ ๋ง๋๋ ๋ฐฉ๋ฒ"๊ณผ ๊ฐ์ ๋ ์ด๋ธ์ด ์๋ ๊ฒ์ด ์ข์ต๋๋ค. ๊ธ์ง ์ฌํญ์ ์์์ ๋ถ๊ณผํ๋ฉฐ ์ฌ๋๋ค์ด "์ด๋ฐ ์ผ์ ํ์ง ์๋ ํ ์์ ํฉ๋๋ค"๋ผ๋ ๊ฒ์ ์๋ฉด์ ๋์๊ด์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
๋ค์ upsert๋ฅผ ๋ง๋ค์์ต๋๋ค. https://gist.github.com/adnanoner/b6c53482243b9d5d5da4e29e109af9bd
๋จ์ผ ๋ฐ ์ผ๊ด upsert๋ฅผ ์ฒ๋ฆฌํฉ๋๋ค. @plurch ์์ ์ฝ๊ฐ ์์ ํ์ต๋๋ค. ๊ฐ์ ์ ํญ์ ๊ฐ์ฌํฉ๋๋ค :)
๊ฐ์น ์๋ ์ผ์ ์ํด ๋ค์ ํ์์ ์ฌ์ฉํ์ต๋๋ค.
ํธ์ง: ์ด๊ฒ์ ๊ฒ์ํ๋ ๋ชจ๋ ์ฌ๋์ ์ํด ์ ๋ฐ์ดํธ๋์์ต๋๋ค. @elhigu ๊ฐ์ฌํฉ๋๋ค
const query = knex( 'account' ).insert( accounts );
const safeQuery = knex.raw( '? ON CONFLICT DO NOTHING', [ query ]);
@NicolajKN toString()์ ์ฌ์ฉํ๋ฉด ์ ๋ฉ๋๋ค. ๋ง์ ์ข ๋ฅ์ ๋ฌธ์ ๋ฅผ ์ผ์ผํฌ ์ ์๊ณ DB ๋ฐ์ธ๋ฉ์ ํตํด ๊ฐ์ ์ ๋ฌํ์ง ์์ต๋๋ค(์ ์ฌ์ ์ธ SQL ์ฃผ์ ๋ณด์ ๊ตฌ๋ฉ).
๊ฐ์ ๋ฐฉ๋ฒ์ผ๋ก ์ฌ๋ฐ๋ฅด๊ฒ ์ํํ๋ฉด ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
const query = knex('account').insert(accounts);
const safeQuery = knex.raw('? ON CONFLICT DO NOTHING', [query]);
๊ด๋ จ ์๋ ๋ฌธ์ ์ ๋ํ ํ ๋ก ์ ์ญ์ ํ์ต๋๋ค.
@elhigu ์ ๊น๋ง ๊ทธ ์ฝ์ ์ฟผ๋ฆฌ๊ฐ ์์ฑ ๋ ์งํ์ ์คํ๋์ง ์์ต๋๊น? ๊ฒฝ์ ์กฐ๊ฑด์ ์์ฑํ์ง ์์ต๋๊น?
@cloutiertyler ๋น์ ์ ๋์๊ฒ ๋ง์ ํ์ง ์์์ง๋ง ์๋ง๋ ๋ด๊ฐ @elhigu ์์ ์๊ฐ์ ์ ์ฝํ ์ ์์ ๊ฒ์
๋๋ค. ์ด๋ฌํ ์ฟผ๋ฆฌ๋ ์คํ๋์ง ์์ต๋๋ค. knex('account').insert(accounts)
๋ฌธ์ ์ฟผ๋ฆฌ๋ฅผ ์คํํ์ง ์์ต๋๋ค. ๋ฐ์ดํฐ๊ฐ ์ค์ ๋ก ํธ์ถ๋ ๋๊น์ง ์คํ๋์ง ์์ต๋๋ค(์: .then
๋ฅผ ํตํด). ๊ทธ๋ ์ด๊ฒ์ query.toString()
ํธ์ถํ knex.raw('? ON CONFLICT DO NOTHING', [query])
๋ก ๋ณด๋ด๊ณ ์ฟผ๋ฆฌ ๋ฅผ ์คํ๋ SQL ๋ฌธ์ผ๋ก ๋ณํํฉ๋๋ค.
@timhuff ๊ฐ์ฌํฉ๋๋ค Tim, ๊ทธ๋ฐ ์์ด์ด์ผ ํ๋ค๊ณ ์๊ฐํ์ง๋ง ์ฝ์์ ๋ํ ์ ์์ ์ธ ํ๋์ ์๋๋๋ค. ์ฝ์์ ์ผ๋ฐ์ ์ผ๋ก ์์ฑ ์ ์คํ๋ฉ๋๋ค. ๋ด๊ฐ ๋ฌป๋ ์ด์ ๋ ์ด upsert๋ฅผ ์คํํ๋ ค๊ณ ํ ๋ "์ฐ๊ฒฐ ์ข ๋ฃ๋จ"์ด๋ผ๋ ์ค๋ฅ๊ฐ ์์ฃผ ๋ฐ์ํ๊ธฐ ๋๋ฌธ์ ๋๋ค. ์ฝ์ ๋ฌผ์ ์ ๊ฑฐํ๊ณ ์์ ํ ์์ ์ฟผ๋ฆฌ๋ฅผ ๋ง๋๋ ๊ฒ์ผ๋ก ์ ํํ๋ฉด ์ฌ๋ผ์ก์ต๋๋ค. ๊ฒฝ์ ์กฐ๊ฑด๊ณผ ์ผ์นํ๋ ๊ฒ์ฒ๋ผ ๋ณด์ ๋๋ค.
knex QueryBuilder
s๋ Promise
s๊ฐ ์๋๋๋ค. knex ์ฟผ๋ฆฌ ์์ฑ์ ์์ํ๋ฉด "knexland"์ ๋จธ๋ฌผ๊ฒ ๋ฉ๋๋ค. ๋น๋ํ๋ ค๋ ์ฟผ๋ฆฌ์ JSON ์ฌ์์ ๊ตฌ์ฑํ๊ธฐ๋ง ํ๋ฉด ๋ฉ๋๋ค. .toString
๋ฅผ ์คํํ๋ฉด ๋น๋ํ๊ณ ์ถ๋ ฅํฉ๋๋ค. ์ด๊ฒ๋ค ์ค ํ๋ ๋ฅผ ์คํํ ๋๊น์ง๋ ( bluebird
) Promise๊ฐ ๋์ง ์์ต๋๋ค. ๋ช
๋ น๋ฌธ์ ์ฆ์ ์คํํ๋ ค๋ฉด .return
๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข์ต๋๋ค.
์, ์๊ฒ ์ต๋๋ค. ๊ทธ๋ฌ๋ฉด ํผ๋์ด ํด์๋ฉ๋๋ค. ์ค๋ช ๊ณผ ํฌ์ธํฐ ์ฃผ์ ์ ๊ฐ์ฌํฉ๋๋ค! ๋ด ๋ฌธ์ ๋ ๋ค๋ฅธ ๊ณณ์ ์์ด์ผ ํฉ๋๋ค.
์ ์ณ๋๊ณ , ์ฆ์ ์คํ ๋์ง ์๋๋ค๋ ์ฌ์ค์ ์ข ์ข ์ ์ฉํฉ๋๋ค. ๋๋๋ก ๋น์ ์ ์คํํ๊ธฐ ์ ์ ๊ทธ๊ฒ์ ๊ตฌ์ฑํ๊ณ ์ ๋ฌํ๊ณ ์ถ์ ๋๊ฐ ์์ต๋๋ค. ๋ค์๊ณผ ๊ฐ์ ์์ ์ ์ํํ ์ ์๋ ์ํฉ๋ ์์ต๋๋ค.
const medicalBuildings = knex.select('building_id').from('buildings').where({type: 'medical'})
const medicalWorkers = knex.select().from('workers').whereIn('building', medicalBuildings)
(๋งค์ฐ ์ธ์์ ์ธ ์์ด์ง๋ง ์คํํด ๋ณด๊ฒ ์ต๋๋ค.)
๋๋ ์ค์ ๋ก ์ฒซ ๋ฒ์งธ ๋ฌธ์ฅ์ ์คํํ๊ณ ์ถ์ง ์์ต๋๋ค. ๊ทธ๊ฒ์ ๋ ๋ฒ์งธ ๋ฌธ์ฅ์ ์ผ๋ถ์ผ ๋ฟ์ ๋๋ค.
๋ชจ๋ ์ฟผ๋ฆฌ ๋น๋๊ฐ ์์ฑ ์ ์คํ๋๋ ๊ฒฝ์ฐ ๋น๋ ํจํด ์ฟผ๋ฆฌ๋ ๋น๋๊ฐ ์๋ฃ๋๊ธฐ ์ ์ ํธ๋ฆฌ๊ฑฐ๋ฉ๋๋ค. ์ฟผ๋ฆฌ๋ฅผ ์คํํ๋ ์ข ๋ฃ์ ๋ฉ์๋๊ฐ ์์ผ๋ฉด ์ ํ ์๋ํ์ง ์์ต๋๋ค.
@elhigu ์ ๋ง์... ํญ์ ๋ค์ ํฑ์์ ์คํํ ์ ์์ ๊ฒ ๊ฐ์๋ฐ์? ๋๋ ๊ทธ๊ฒ์ด ์ข์ ์๊ฐ์ด ๋ ๊ฒ์ด๋ผ๊ณ ์ ์ํ๋ ๊ฒ์ด ์๋์ง๋ง ์ค์ ๋ก ์ผ๋ง๋ ๋ง์ ์ฟผ๋ฆฌ๊ฐ ์์ฑ๋๊ณ ๋ค๋ฅธ ํฑ์์ ์คํ๋ฉ๋๊น?
@timhuff ๋๋ ๊ทธ๊ฒ์ ๋ํด ์๊ฐํ์ง ์์์ต๋๋ค. ๋ค ๊ทธ๊ฒ๋ ๊ฐ๋ฅํ ๊ฒ ๊ฐ์์. ์ฟผ๋ฆฌ ์์ฑ์ ์์ํ ๋ค์ ๋น๋๊ธฐ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๊ณ ๊ณ์ ๋น๋ํ๋ ๊ฒฝ์ฐ๊ฐ ์์ฃผ ํํฉ๋๋ค. ๊ทธ๋ ๊ฒ ์์ฃผ ํ์ง๋ ์์ง๋ง.
@lukewlms ๋ 'execute()'์ ๊ฐ์ ๋ฉ์๋๋ฅผ '.then()'์ด๋ผ๊ณ ํ๋ฉฐ ์ฟผ๋ฆฌ๋ฅผ ์คํํ๊ณ ์ฝ์์ ๋ฐ๊ณ ์ถ์ ๋ ํญ์ ํธ์ถํ ์ ์์ต๋๋ค. 'thenable'์ด ์๋ํ๋ ๋ฐฉ์์ด๋ฉฐ promise spec์ ์ค๋ช ๋์ด ์์ต๋๋ค. ์ด๊ฒ์ Promise์ async/await(Promise.resolve ๋ฐ .then์ ๋ํ ๊ฑฐ์ ์๊ด์ค๋ฌ์ด ๋ฐ๋ก ๊ฐ๊ธฐ)๋ฅผ ์ฒ๋ฆฌํ ๋ ์๋ฐ์คํฌ๋ฆฝํธ์์ ์ค์ํ๊ณ ๋๋ฆฌ ์ฌ์ฉ๋๋ ๊ฐ๋ ์ค ํ๋์ ๋๋ค. ๋ํ ๊ฒฐ๊ณผ๋ฅผ ์ฒ๋ฆฌํ์ง ์๊ณ ์ฟผ๋ฆฌ๋ฅผ ์คํํ๋ ๊ฒฝ์ฐ ์ฑ ์ถฉ๋๊ณผ ๊ฐ์ ๋ฌธ์ ๋ฅผ ์ฐพ๊ณ ์์ต๋๋ค.
์ค์ ๋ก upsert ๊ธฐ๋ฅ ๊ตฌํ https://github.com/tgriesser/knex/pull/2197 ์ ๋ํ ์ด PR์ ๋ฐ๋ฅด๋ ๊ฒ์ด ๋ ๋ซ์ต๋๋ค. API๋ ์ด๋ฏธ ์๋ ๋ฐฉ์์ ์ค๊ณํ์ต๋๋ค. ์ด ์ค๋ ๋์๋ ํด๋น PR์ ์ฃผ์์์ ์ด๋ฏธ ์ธ๊ธ๋์ง ์์ ์ ์ฉํ ์ ๋ณด๊ฐ ์์ต๋๋ค. ํ์ํ ๊ฒฝ์ฐ(PR์ด ์ข ๋ฃ๋๊ณ ์๋ฃ๋์ง ์์) ์ถ๊ฐ API ์ค๋ช ๊ณผ ํจ๊ป ์ด ๋ฌธ์ ์ ๋ํ ์ ๋ฌธ์ ๋ฅผ ์ด โโ์ ์์ต๋๋ค.
@elhigu ์๋ ค ์ฃผ์ ์ ๊ฐ์ฌํฉ๋๋ค! ๋๋ ๊ทธ ์ค๋ ๋๋ฅผ ๋ชฐ๋๋ค. API์ ๋ํ upsert๊ฐ ์งํ ์ค์ด๋ผ๋ ์์์ ๋ฃ๊ฒ ๋์ด ๊ธฐ์ฉ๋๋ค. 6๊ฐ์ ์ ์ 802 ํ ์คํธ ์ค 1๊ฐ์ ์คํจํ์ฌ travis-ci๋ฅผ ํต๊ณผํ์ง ๋ชปํ ๊ฒ ๊ฐ์ต๋๋ค. ์ด๊ฒ์ด knex API์ ์ผ๋ถ๊ฐ ๋์ง ์๋๋ก ํ๋ ์ ์ผํ ๋ฐฉ๋ฒ์ ์คํจํ ํ ์คํธ ์ผ์ด์ค 1๊ฐ์ ๋๊น?
@timhuff ์ด๊ธฐ ๊ตฌํ๋ง ์๋ฃ๋์์ผ๋ฏ๋ก ์์ ํ ๋ค์ ์์ฑํด์ผ ํฉ๋๋ค. ๊ทธ PR์ ๊ฐ์ฅ ์ค์ํ ๋ถ๋ถ์ ๋๋ถ๋ถ์ ๋ฐฉ์ธ์์ ์ง์ํ ์ ์๋ ๊ณตํต API ๋์์ธ์ ๋๋ค. ๋ฐ๋ผ์ ์ด ๊ธฐ๋ฅ์ ๋๊ตฐ๊ฐ๊ฐ ํด๋น API๋ฅผ ๊ตฌํํ๊ธฐ๋ก ๊ฒฐ์ ํ ๋ ์ ๊ณต๋ฉ๋๋ค. ์๋ฌด๋ ๊ทธ๋ ๊ฒ ํ์ง ์๊ณ ์ธ์ ๊ฐ ๋์๊ฒ ์ฌ๋ถ์ ์๊ฐ์ด ์๊ธฐ๊ฑฐ๋ ์ ์คํ ํ์ํ๋ค๋ฉด ๋ด๊ฐ ์ง์ ํ ๊ฒ์ ๋๋ค. ์ด๊ฒ์ (์ ๋ฐ์ดํธ์ ์ฐธ์ฌํ๋ ๊ฒ ์ธ์๋) knex๊ฐ ์ป๊ณ ์ ํ๋ ๊ฐ์ฅ ์ค์ํ ๊ธฐ๋ฅ ์ค ํ๋์ ๋๋ค.
@elhigu ์์ฑํด ์ฃผ์ ์ ๊ฐ์ฌํฉ๋๋ค. ์๊ฐ์ด ์ข ๋ ์์ผ๋ฉด ์ฌ๊ธฐ์์ ์งํ ์ํฉ์ ์ฝ์ด๋ด์ผ๊ฒ ์ต๋๋ค.
์ด๊ฒ์ด ๋๊ตฐ๊ฐ์๊ฒ ๋์์ด ๋๋์ง ์๋๋ฉด ์ ๊ฐ ๋ฉ์ฒญํ ๋์ธ์ง ํ์คํ์ง ์์ง๋ง @timhuff ์ ์๋ฃจ์ ์ ๋ํด ์ฟผ๋ฆฌ ๊ตฌ๋ฌธ ์ค๋ฅ๊ฐ ๋ฐ์ํ๊ธฐ ๋๋ฌธ์ ์ ์ฝ ์กฐ๊ฑด์ ๋ฐ์ดํ๋ก ๋ฌถ์ด์ผ ํ์ต๋๋ค.
const contraint = '("a", "b")'
๋๋ knex ๊ธฐ๋ฐ Postgres upsert๋ฅผ ์ฐพ์ ์ด ๋ฌธ์ ์ ์ฌ๋ฌ ๋ฒ ์์ต๋๋ค. ๋ค๋ฅธ ์ฌ๋์ด ์ด๊ฒ์ ํ์๋ก ํ๋ ๊ฒฝ์ฐ ๋ฐฉ๋ฒ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค. ๋จ์ผ ๋ฐ ๋ณตํฉ ๊ณ ์ ํค์ ๋ํด ์ด๊ฒ์ ํ ์คํธํ์ต๋๋ค.
์ค์
์๋๋ฅผ ์ฌ์ฉํ์ฌ ํ ์ด๋ธ์ ๊ณ ์ ํค ์ ์ฝ ์กฐ๊ฑด์ ๋ง๋ญ๋๋ค. ๋ณตํฉ ํค ์ ์ฝ ์กฐ๊ฑด์ด ํ์ํ์ต๋๋ค.
table.unique(['a', 'b'])
ํจ์
(ํธ์ง: ์์ ๋งค๊ฐ๋ณ์ ๋ฐ์ธ๋ฉ์ ์ฌ์ฉํ๋๋ก ์ ๋ฐ์ดํธ๋จ)
const upsert = (params)=> { const {table, object, constraint} = params; const insert = knex(table).insert(object); const update = knex.queryBuilder().update(object); return knex.raw(`? ON CONFLICT ${constraint} DO ? returning *`, [insert, update]).get('rows').get(0); };
์ฉ๋ฒ
const objToUpsert = {a:1, b:2, c:3} upsert({ table: 'test', object: objToUpsert, constraint: '(a, b)', })
์ ์ฝ ์กฐ๊ฑด์ด ํฉ์ฑ๋์ง ์์ ๊ฒฝ์ฐ ๋น์ฐํ ํ ์ค์
constraint: '(a)'
์ ๋๋ค.์ ๋ฐ์ดํธ๋ ๊ฐ์ฒด ๋๋ ์ฝ์ ๋ ๊ฐ์ฒด๋ฅผ ๋ฐํํฉ๋๋ค.
๋ณตํฉ nullable ์ธ๋ฑ์ค์ ๋ํ ์ฐธ๊ณ ์ฌํญ
๋ณตํฉ ์ธ๋ฑ์ค
(a,b)
๊ฐ ์๊ณb
์ด nullable์ด๋ฉด(1, NULL)
๋ฐ(1, NULL)
๊ฐ์ Postgres์์ ์ํธ ๊ณ ์ ํ ๊ฒ์ผ๋ก ๊ฐ์ฃผ๋ฉ๋๋ค(์ดํดํ ์ ์์ต๋๋ค. ์ด๋ ํ๋). ์ด๊ฒ์ด ์ฌ์ฉ ์ฌ๋ก์ธ ๊ฒฝ์ฐ ๋ถ๋ถ ๊ณ ์ ์ธ๋ฑ์ค๋ฅผ ๋ง๋ ๋ค์ upsert ์ ์ null์ ํ ์คํธํ์ฌ ์ฌ์ฉํ ์ ์ฝ ์กฐ๊ฑด์ ๊ฒฐ์ ํด์ผ ํฉ๋๋ค. ๋ถ๋ถ ๊ณ ์ ์ธ๋ฑ์ค๋ฅผ ๋ง๋๋ ๋ฐฉ๋ฒ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.CREATE UNIQUE INDEX unique_index_name ON table (a) WHERE b IS NULL
. ํ ์คํธ์์b
๊ฐ null์ธ ๊ฒ์ผ๋ก ํ์ธ๋๋ฉด upsert์์constraint: '(a) WHERE b IS NULL'
์ ์ฝ ์กฐ๊ฑด์ ์ฌ์ฉํด์ผ ํฉ๋๋ค.a
๋ nullable์ด๋ฉด 3๊ฐ์ ๊ณ ์ ์ธ๋ฑ์ค์ 4๊ฐ์if/else
๋ถ๊ธฐ๊ฐ ํ์ํ๋ค๊ณ ์๊ฐํฉ๋๋ค(๋ด ์ฌ์ฉ ์ฌ๋ก๊ฐ ์๋๋ฏ๋ก ํ์คํ์ง ์์).๋ค์์ ์ปดํ์ผ๋ ์๋ฐ์คํฌ๋ฆฝํธ ์ ๋๋ค.
๋๊ตฐ๊ฐ๊ฐ ์ด๊ฒ์ ์ ์ฉํ๊ฒ ์ฌ์ฉํ๊ธฐ๋ฅผ ๋ฐ๋๋๋ค. @elhigu ~
knex().update(object)
์ฌ์ฉ์ ๋ํ ์๊ฒฌ ?~ (ํธ์ง: Nevermind - ๊ฒฝ๊ณ ๋ฅผ ๋ณด์์ - ์ง๊ธknex.queryBuilder()
์ฌ์ฉ)
์ผ๋ถ ๊ด๋ จ ์๋ ํ ๋ก ์ ์ ๊ฑฐํ์ต๋๋ค(promise/thenables ์๋ ๋ฐฉ์์ ๋ํด).
์ด ์ถ๊ฐ ๋์์ต๋๊น?
์๋์. https://github.com/knex/knex/issues/3186 ์ ๊ธฐ๋ฅ ์์ฒญ ๋ฐ ์ฌ์์ด ์์ต๋๋ค.
๊ฐ์ฅ ์ ์ฉํ ๋๊ธ
@NicolajKN toString()์ ์ฌ์ฉํ๋ฉด ์ ๋ฉ๋๋ค. ๋ง์ ์ข ๋ฅ์ ๋ฌธ์ ๋ฅผ ์ผ์ผํฌ ์ ์๊ณ DB ๋ฐ์ธ๋ฉ์ ํตํด ๊ฐ์ ์ ๋ฌํ์ง ์์ต๋๋ค(์ ์ฌ์ ์ธ SQL ์ฃผ์ ๋ณด์ ๊ตฌ๋ฉ).
๊ฐ์ ๋ฐฉ๋ฒ์ผ๋ก ์ฌ๋ฐ๋ฅด๊ฒ ์ํํ๋ฉด ๋ค์๊ณผ ๊ฐ์ต๋๋ค.