晴明的博客园 GitHub      CodePen      CodeWars     

[db] sequelize

#

var User = sequelize.define(
    // 默认表名(一般这里写单数),生成时会自动转换成复数形式
    // 这个值还会作为访问模型相关的模型时的属性名,所以建议用小写形式
    'user',
    // 字段定义(主键、created_at、updated_at默认包含,不用特殊定义)
    {
        'emp_id': {
            'type': Sequelize.CHAR(10), // 字段类型
            'allowNull': false,         // 是否允许为NULL
            'unique': true              // 字段是否UNIQUE
        },
        'nick': {
            'type': Sequelize.CHAR(10),
            'allowNull': false
        },
        'department': {
            'type': Sequelize.STRING(64),
            'allowNull': true
        }
    }
);

var User = sequelize.define(
    'user',
    {
        'emp_id': {
            'type': Sequelize.CHAR(10), // 字段类型
            'allowNull': false,         // 是否允许为NULL
            'unique': true              // 字段是否UNIQUE
        },
        'nick': {
            'type': Sequelize.CHAR(10),
            'allowNull': false
        },
        'department': {
            'type': Sequelize.STRING(64),
            'allowNull': true
        }
    },
    {
        // 自定义表名
        'freezeTableName': true,
        'tableName': 'xyz_users',

        // 是否需要增加createdAt、updatedAt、deletedAt字段
        'timestamps': true,

        // 不需要createdAt字段
        'createdAt': false,

        // 将updatedAt字段改个名
        'updatedAt': 'utime'

        // 将deletedAt字段改名
        // 同时需要设置paranoid为true(此种模式下,删除数据时不会进行物理删除,而是设置deletedAt为当前时间
        'deletedAt': 'dtime',
        'paranoid': true
    }
);

SQL:

CREATE TABLE IF NOT EXISTS `users` (
    `id` INTEGER NOT NULL auto_increment ,
    `emp_id` CHAR(10) NOT NULL UNIQUE,
    `nick` CHAR(10) NOT NULL,
    `department` VARCHAR(64),
    `created_at` DATETIME NOT NULL,
    `updated_at` DATETIME NOT NULL,
    PRIMARY KEY (`id`)
) ENGINE=InnoDB;

建表SQL会自动执行的意思是主动调用sync的时候。
类似这样:User.sync({force: true});(加force:true,会先删掉表后再建表)。
也可以先定义好表结构,再来定义Sequelize模型,这时可以不用sync。
两者在定义阶段没有什么关系,直到我们真正开始操作模型时,才会触及到表的操作,
但是我们当然还是要尽量保证模型和表的同步(可以借助一些migration工具)。
自动建表功能有风险,使用需谨慎。

获取“干净”的JSON对象可以调用get({'plain': true})。




Sequelize:

// 方法1:build后对象只存在于内存中,调用save后才操作db
var user = User.build({
    'emp_id': '1',
    'nick': '小红',
    'department': '技术部'
});
user = yield user.save();
console.log(user.get({'plain': true}));

// 方法2:直接操作db
var user = yield User.create({
    'emp_id': '2',
    'nick': '小明',
    'department': '技术部'
});
console.log(user.get({'plain': true}));

SQL:

INSERT INTO `users`
(`id`, `emp_id`, `nick`, `department`, `updated_at`, `created_at`)
VALUES
(DEFAULT, '1', '小红', '技术部', '2015-11-02 14:49:54', '2015-11-02 14:49:54');




Sequelize:

// 方法1:操作对象属性(不会操作db),调用save后操作db
user.nick = '小白';
user = yield user.save();
console.log(user.get({'plain': true}));

// 方法2:直接update操作db
user = yield user.update({
    'nick': '小白白'
});
console.log(user.get({'plain': true}));

SQL:

UPDATE `users`
SET `nick` = '小白白', `updated_at` = '2015-11-02 15:00:04'
WHERE `id` = 1;


限制更新属性的白名单

// 方法1
user.emp_id = '33';
user.nick = '小白';
user = yield user.save({'fields': ['nick']});

// 方法2
user = yield user.update(
    {'emp_id': '33', 'nick': '小白'},
    {'fields': ['nick']}
});




Sequelize:

yield user.destroy();

SQL:

DELETE FROM `users` WHERE `id` = 1;

查全部

Sequelize:

var users = yield User.findAll();
console.log(users);

SQL:

SELECT `id`, `emp_id`, `nick`, `department`, `created_at`, `updated_at` FROM `users`;

限制字段

Sequelize:

var users = yield User.findAll({
    'attributes': ['emp_id', 'nick']
});
console.log(users);

SQL:

SELECT `emp_id`, `nick` FROM `users`;

字段重命名

Sequelize:

var users = yield User.findAll({
    'attributes': [
        'emp_id', ['nick', 'user_nick']
    ]
});
console.log(users);

SQL:

SELECT `emp_id`, `nick` AS `user_nick` FROM `users`;


where子句

基本条件

Sequelize:

var users = yield User.findAll({
    'where': {
        'id': [1, 2, 3],
        'nick': 'a',
        'department': null
    }
});
console.log(users);

SQL:

SELECT `id`, `emp_id`, `nick`, `department`, `created_at`, `updated_at`
FROM `users` AS `user`
WHERE
    `user`.`id` IN (1, 2, 3) AND
    `user`.`nick`='a' AND
    `user`.`department` IS NULL;
    
操作符

操作符是对某个字段的进一步约束,可以有多个(对同一个字段的多个操作符会被转化为AND)。

Sequelize:

var users = yield User.findAll({
    'where': {
        'id': {
            '$eq': 1,                // id = 1
            '$ne': 2,                // id != 2

            '$gt': 6,                // id > 6
            '$gte': 6,               // id >= 6

            '$lt': 10,               // id < 10
            '$lte': 10,              // id <= 10

            '$between': [6, 10],     // id BETWEEN 6 AND 10
            '$notBetween': [11, 15], // id NOT BETWEEN 11 AND 15

            '$in': [1, 2],           // id IN (1, 2)
            '$notIn': [3, 4]         // id NOT IN (3, 4)
        },
        'nick': {
            '$like': '%a%',          // nick LIKE '%a%'
            '$notLike': '%a'         // nick NOT LIKE '%a'
        },
        'updated_at': {
            '$eq': null,             // updated_at IS NULL
            '$ne': null              // created_at IS NOT NULL
        }
    }
});

SQL:

SELECT `id`, `emp_id`, `nick`, `department`, `created_at`, `updated_at`
FROM `users` AS `user`
WHERE
(
    `user`.`id` = 1 AND
    `user`.`id` != 2 AND
    `user`.`id` > 6 AND
    `user`.`id` >= 6 AND
    `user`.`id` < 10 AND
    `user`.`id` <= 10 AND
    `user`.`id` BETWEEN 6 AND 10 AND
    `user`.`id` NOT BETWEEN 11 AND 15 AND
    `user`.`id` IN (1, 2) AND
    `user`.`id` NOT IN (3, 4)
)
AND
(
    `user`.`nick` LIKE '%a%' AND
    `user`.`nick` NOT LIKE '%a'
)
AND
(
    `user`.`updated_at` IS NULL AND
    `user`.`updated_at` IS NOT NULL
);

条件

上面说的条件查询,都是AND查询,Sequelize同时也支持OR、NOT、甚至多种条件的联合查询。

AND条件

Sequelize:

var users = yield User.findAll({
    'where': {
        '$and': [
            {'id': [1, 2]},
            {'nick': null}
        ]
    }
});

SQL:

SELECT `id`, `emp_id`, `nick`, `department`, `created_at`, `updated_at`
FROM `users` AS `user`
WHERE
(
    `user`.`id` IN (1, 2) AND
    `user`.`nick` IS NULL
);    

OR条件

Sequelize:

var users = yield User.findAll({
    'where': {
        '$or': [
            {'id': [1, 2]},
            {'nick': null}
        ]
    }
});

SQL:

SELECT `id`, `emp_id`, `nick`, `department`, `created_at`, `updated_at`
FROM `users` AS `user`
WHERE
(
    `user`.`id` IN (1, 2) OR
    `user`.`nick` IS NULL
);

NOT条件

Sequelize:

var users = yield User.findAll({
    'where': {
        '$not': [
            {'id': [1, 2]},
            {'nick': null}
        ]
    }
});

SQL:

SELECT `id`, `emp_id`, `nick`, `department`, `created_at`, `updated_at`
FROM `users` AS `user`
WHERE
NOT (
    `user`.`id` IN (1, 2) AND
    `user`.`nick` IS NULL
);

转换规则

Sequelize对where配置的转换规则的伪代码大概如下:

function translate(where) {

    for (k, v of where) {

        if (k == 表字段) {
            // 先统一转为操作符形式
            if (v == 基本值) { // k: 'xxx'
                v = {'$eq': v};
            }
            if (v == 数组) { // k: [1, 2, 3]
                v = {'$in': v};
            }

            // 操作符转换
            for (opk, opv of v) {
                // op将opk转换对应的SQL表示
                => k + op(opk, opv) + AND;
            }
        }

        // 逻辑操作符处理

        if (k == '$and') {
            for (item in v) {
                => translate(item) + AND;
            }
        }

        if (k == '$or') {
            for (item in v) {
                => translate(item) + OR;
            }
        }

        if (k == '$not') {
            NOT +
            for (item in v) {
                => translate(item) + AND;
            }
        }

    }

    function op(opk, opv) {
        switch (opk) {
            case $eq => ('=' + opv) || 'IS NULL';
            case $ne => ('!=' + opv) || 'IS NOT NULL';
            case $gt => '>' + opv;
            case $lt => '<' + opv;
            case $gte => '>=' + opv;
            case $lte => '<=' + opv;
            case $between => 'BETWEEN ' + opv[0] + ' AND ' + opv[1];
            case $notBetween => 'NOT BETWEEN ' + opv[0] + ' AND ' + opv[1];
            case $in => 'IN (' + opv.join(',') + ')';
            case $notIn => 'NOT IN (' + opv.join(',') + ')';
            case $like => 'LIKE ' + opv;
            case $notLike => 'NOT LIKE ' + opv;
        }
    }

}

一个复杂例子,基本上就是按上述流程来进行转换。

Sequelize:

var users = yield User.findAll({
    'where': {
        'id': [3, 4],
        '$not': [
            {
                'id': {
                    '$in': [1, 2]
                }
            },
            {
                '$or': [
                    {'id': [1, 2]},
                    {'nick': null}
                ]
            }
        ],
        '$and': [
            {'id': [1, 2]},
            {'nick': null}
        ],
        '$or': [
            {'id': [1, 2]},
            {'nick': null}
        ]
    }
});

SQL:

SELECT `id`, `emp_id`, `nick`, `department`, `created_at`, `updated_at`
FROM `users` AS `user`
WHERE
    `user`.`id` IN (3, 4)
AND
NOT
(
    `user`.`id` IN (1, 2)
    AND
    (`user`.`id` IN (1, 2) OR `user`.`nick` IS NULL)
)
AND
(
    `user`.`id` IN (1, 2) AND `user`.`nick` IS NULL
)
AND
(
    `user`.`id` IN (1, 2) OR `user`.`nick` IS NULL
);

排序

Sequelize:

var users = yield User.findAll({
    'order': [
        ['id', 'DESC'],
        ['nick']
    ]
});

SQL:

SELECT `id`, `emp_id`, `nick`, `department`, `created_at`, `updated_at`
FROM `users` AS `user`
ORDER BY `user`.`id` DESC, `user`.`nick`;

分页

Sequelize:

var countPerPage = 20, currentPage = 5;
var users = yield User.findAll({
    'limit': countPerPage,                      // 每页多少条
    'offset': countPerPage * (currentPage - 1)  // 跳过多少条
});

SQL:

SELECT `id`, `emp_id`, `nick`, `department`, `created_at`, `updated_at`
FROM `users` AS `user`
LIMIT 80, 20;

查询一条数据

Sequelize:

user = yield User.findById(1);

user = yield User.findOne({
    'where': {'nick': 'a'}
});

SQL:

SELECT `id`, `emp_id`, `nick`, `department`, `created_at`, `updated_at`
FROM `users` AS `user`
WHERE `user`.`id` = 1 LIMIT 1;

SELECT `id`, `emp_id`, `nick`, `department`, `created_at`, `updated_at`
FROM `users` AS `user`
WHERE `user`.`nick` = 'a' LIMIT 1;

查询并获取数量

Sequelize:

var result = yield User.findAndCountAll({
    'limit': 20,
    'offset': 0
});
console.log(result);//result.count是数据总数,result.rows是符合查询条件的所有数据

SQL:

SELECT count(*) AS `count` FROM `users` AS `user`;

SELECT `id`, `emp_id`, `nick`, `department`, `created_at`, `updated_at`
FROM `users` AS `user`
LIMIT 20;

批量操作

插入

Sequelize:

var users = yield User.bulkCreate(
    [
        {'emp_id': 'a', 'nick': 'a'},
        {'emp_id': 'b', 'nick': 'b'},
        {'emp_id': 'c', 'nick': 'c'}
    ]
);

SQL:

INSERT INTO `users`
    (`id`,`emp_id`,`nick`,`created_at`,`updated_at`)
VALUES
    (NULL,'a','a','2015-11-03 02:43:30','2015-11-03 02:43:30'),
    (NULL,'b','b','2015-11-03 02:43:30','2015-11-03 02:43:30'),
    (NULL,'c','c','2015-11-03 02:43:30','2015-11-03 02:43:30');

这里需要注意,返回的users数组里面每个对象的id值会是null。
如果需要id值,可以重新取下数据。

更新

Sequelize:

var affectedRows = yield User.update(
    {'nick': 'hhhh'},
    {
        'where': {
            'id': [2, 3, 4]
        }
    }
);

SQL:

UPDATE `users`
SET `nick`='hhhh',`updated_at`='2015-11-03 02:51:05'
WHERE `id` IN (2, 3, 4);

这里返回的affectedRows其实是一个数组,里面只有一个元素,表示更新的数据条数

删除

Sequelize:

var affectedRows = yield User.destroy({
    'where': {'id': [2, 3, 4]}
});

SQL:

DELETE FROM `users` WHERE `id` IN (2, 3, 4);

这里返回的affectedRows是一个数字,表示删除的数据条数。

关系

一对一
模型定义

Sequelize:

var User = sequelize.define('user',
    {
        'emp_id': {
            'type': Sequelize.CHAR(10),
            'allowNull': false,
            'unique': true
        }
    }
);
var Account = sequelize.define('account',
    {
        'email': {
            'type': Sequelize.CHAR(20),
            'allowNull': false
        }
    }
);

/*
 * User的实例对象将拥有getAccount、setAccount、addAccount方法
 */
User.hasOne(Account);
/*
 * Account的实例对象将拥有getUser、setUser、addUser方法
 */
Account.belongsTo(User);

SQL:

CREATE TABLE IF NOT EXISTS `users` (
    `id` INTEGER NOT NULL auto_increment ,
    `emp_id` CHAR(10) NOT NULL UNIQUE,
    `created_at` DATETIME NOT NULL,
    `updated_at` DATETIME NOT NULL,
    PRIMARY KEY (`id`)
) ENGINE=InnoDB;

CREATE TABLE IF NOT EXISTS `accounts` (
    `id` INTEGER NOT NULL auto_increment ,
    `email` CHAR(20) NOT NULL,
    `created_at` DATETIME NOT NULL,
    `updated_at` DATETIME NOT NULL,
    `user_id` INTEGER,
    PRIMARY KEY (`id`),
    FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE SET NULL ON UPDATE CASCADE
) ENGINE=InnoDB;

可以看到,这种关系中外键user_id加在了Account上。
另外,Sequelize还生成了外键约束。
一般来说,外键约束在有些自己定制的数据库系统里面是禁止的,因为会带来一些性能问题。
所以,建表的SQL一般就去掉约束,同时给外键加一个索引(加速查询),
数据的一致性就靠应用层来保证了。



Sequelize:

var user = yield User.create({'emp_id': '1'});
var account = user.createAccount({'email': 'a'});
console.log(account.get({'plain': true}));

SQL:

INSERT INTO `users`
(`id`,`emp_id`,`updated_at`,`created_at`)
VALUES
(DEFAULT,'1','2015-11-03 06:24:53','2015-11-03 06:24:53');

INSERT INTO `accounts`
(`id`,`email`,`user_id`,`updated_at`,`created_at`)
VALUES
(DEFAULT,'a',1,'2015-11-03 06:24:53','2015-11-03 06:24:53');

SQL执行逻辑是:
    使用对应的的user_id作为外键在accounts表里插入一条数据。
    


Sequelize:

var anotherAccount = yield Account.create({'email': 'b'});
console.log(anotherAccount);
anotherAccount = yield user.setAccount(anotherAccount);
console.log(anotherAccount);

SQL:

INSERT INTO `accounts`
(`id`,`email`,`updated_at`,`created_at`)
VALUES
(DEFAULT,'b','2015-11-03 06:37:14','2015-11-03 06:37:14');

SELECT `id`, `email`, `created_at`, `updated_at`, `user_id`
FROM `accounts` AS `account` WHERE (`account`.`user_id` = 1);

UPDATE `accounts` SET `user_id`=NULL,`updated_at`='2015-11-03 06:37:14' WHERE `id` = 1;
UPDATE `accounts` SET `user_id`=1,`updated_at`='2015-11-03 06:37:14' WHERE `id` = 2;

SQL执行逻辑是:

    插入一条account数据,此时外键user_id是空的,还没有关联user
    找出当前user所关联的account并将其user_id置为`NUL(为了保证一对一关系)
    设置新的acount的外键user_id为user的属性id,生成关系



Sequelize:

yield user.setAccount(null);

SQL:

SELECT `id`, `email`, `created_at`, `updated_at`, `user_id`
FROM `accounts` AS `account`
WHERE (`account`.`user_id` = 1);

UPDATE `accounts`
SET `user_id`=NULL,`updated_at`='2015-11-04 00:11:35'
WHERE `id` = 1;

这里的删除实际上只是“切断”关系,并不会真正的物理删除记录。

SQL执行逻辑是:
    找出user所关联的account数据
    将其外键user_id设置为NULL,完成关系的“切断”


情况1

查询user的所有满足条件的note数据。

Sequelize:

var notes = yield user.getNotes({
    'where': {
        'title': {
            '$like': '%css%'
        }
    }
});
notes.forEach(function(note) {
    console.log(note);
});

SQL:

SELECT `id`, `title`, `created_at`, `updated_at`, `user_id`
FROM `notes` AS `note`
WHERE (`note`.`user_id` = 1 AND `note`.`title` LIKE '%a%');

这种方法的SQL很简单,直接根据user的id值来查询满足条件的note即可。

情况2

查询所有满足条件的note,同时获取note属于哪个user。

Sequelize:

var notes = yield Note.findAll({
    'include': [User],
    'where': {
        'title': {
            '$like': '%css%'
        }
    }
});
notes.forEach(function(note) {
    // note属于哪个user可以通过note.user访问
    console.log(note);
});

SQL:

SELECT `note`.`id`, `note`.`title`, `note`.`created_at`, `note`.`updated_at`, `note`.`user_id`,
`user`.`id` AS `user.id`, `user`.`emp_id` AS `user.emp_id`, `user`.`created_at` AS `user.created_at`, `user`.`updated_at` AS `user.updated_at`
FROM `notes` AS `note` LEFT OUTER JOIN `users` AS `user`
ON `note`.`user_id` = `user`.`id`
WHERE `note`.`title` LIKE '%css%';

这种方法,因为获取的主体是note,所以将notes去left join了users。    

情况3

查询所有满足条件的user,同时获取该user所有满足条件的note。

Sequelize:

var users = yield User.findAll({
    'include': [Note],
    'where': {
        'created_at': {
            '$lt': new Date()
        }
    }
});
users.forEach(function(user) {
    // user的notes可以通过user.notes访问
    console.log(user);
});

SQL:

SELECT `user`.`id`, `user`.`emp_id`, `user`.`created_at`, `user`.`updated_at`,
`notes`.`id` AS `notes.id`, `notes`.`title` AS `notes.title`, `notes`.`created_at` AS `notes.created_at`, `notes`.`updated_at` AS `notes.updated_at`, `notes`.`user_id` AS `notes.user_id`
FROM `users` AS `user` LEFT OUTER JOIN `notes` AS `notes`
ON `user`.`id` = `notes`.`user_id`
WHERE `user`.`created_at` < '2015-11-05 01:51:35';

这种方法获取的主体是user,所以将users去left join了notes。


关于eager loading。
include里面传递的是去取相关模型,默认是取全部,也可以再对这个模型进行一层过滤。
Sequelize:

// 查询创建时间在今天之前的所有user,同时获取他们note的标题中含有关键字css的所有note
var users = yield User.findAll({
    'include': [
        {
            'model': Note,
            'where': {
                'title': {
                    '$like': '%css%'
                }
            }
        }
    ],
    'where': {
        'created_at': {
            '$lt': new Date()
        }
    }
});

SQL:

SELECT `user`.`id`, `user`.`emp_id`, `user`.`created_at`, `user`.`updated_at`,
`notes`.`id` AS `notes.id`, `notes`.`title` AS `notes.title`, `notes`.`created_at` AS `notes.created_at`, `notes`.`updated_at` AS `notes.updated_at`, `notes`.`user_id` AS `notes.user_id`
FROM `users` AS `user` INNER JOIN `notes` AS `notes`
ON `user`.`id` = `notes`.`user_id` AND `notes`.`title` LIKE '%css%'
WHERE `user`.`created_at` < '2015-11-05 01:58:31';

注意:当对include的模型加了where过滤时,会使用inner join来进行查询,
这样保证只有那些拥有标题含有css关键词note的用户才会返回。

多对多关系

模型定义

Sequelize:

var Note = sequelize.define('note',
    {
        'title': {
            'type': Sequelize.CHAR(64),
            'allowNull': false
        }
    }
);
var Tag = sequelize.define('tag',
    {
        'name': {
            'type': Sequelize.CHAR(64),
            'allowNull': false,
            'unique': true
        }
    }
);
var Tagging = sequelize.define('tagging',
    {
        'type': {
            'type': Sequelize.INTEGER(),
            'allowNull': false
        }
    }
);

// Note的实例拥有getTags、setTags、addTag、addTags、createTag、removeTag、hasTag方法
Note.belongsToMany(Tag, {'through': Tagging});
// Tag的实例拥有getNotes、setNotes、addNote、addNotes、createNote、removeNote、hasNote方法
Tag.belongsToMany(Note, {'through': Tagging});

SQL:

CREATE TABLE IF NOT EXISTS `notes` (
    `id` INTEGER NOT NULL auto_increment ,
    `title` CHAR(64) NOT NULL,
    `created_at` DATETIME NOT NULL,
    `updated_at` DATETIME NOT NULL,
    PRIMARY KEY (`id`)
) ENGINE=InnoDB;

CREATE TABLE IF NOT EXISTS `tags` (
    `id` INTEGER NOT NULL auto_increment ,
    `name` CHAR(64) NOT NULL UNIQUE,
    `created_at` DATETIME NOT NULL,
    `updated_at` DATETIME NOT NULL,
    PRIMARY KEY (`id`)
) ENGINE=InnoDB;

CREATE TABLE IF NOT EXISTS `taggings` (
    `type` INTEGER NOT NULL,
    `created_at` DATETIME NOT NULL,
    `updated_at` DATETIME NOT NULL,
    `tag_id` INTEGER ,
    `note_id` INTEGER ,
    PRIMARY KEY (`tag_id`, `note_id`),
    FOREIGN KEY (`tag_id`) REFERENCES `tags` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
    FOREIGN KEY (`note_id`) REFERENCES `notes` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB;

多对多关系中单独生成了一张关系表,并设置了2个外键tag_id和note_id来和tags和notes进行关联。


方法1

Sequelize:

var note = yield Note.create({'title': 'note'});
yield note.createTag({'name': 'tag'}, {'type': 0});

SQL:

INSERT INTO `notes`
(`id`,`title`,`updated_at`,`created_at`)
VALUES
(DEFAULT,'note','2015-11-06 02:14:38','2015-11-06 02:14:38');

INSERT INTO `tags`
(`id`,`name`,`updated_at`,`created_at`)
VALUES
(DEFAULT,'tag','2015-11-06 02:14:38','2015-11-06 02:14:38');

INSERT INTO `taggings`
(`tag_id`,`note_id`,`type`,`created_at`,`updated_at`)
VALUES
(1,1,0,'2015-11-06 02:14:38','2015-11-06 02:14:38');

SQL执行逻辑:

    在notes表插入记录

    在tags表中插入记录

    使用对应的值设置外键tag_id和note_id以及关系模型本身需要的属性(type: 0)在关系表tagging中插入记录

关系表本身需要的属性,通过传递一个额外的对象给设置方法来实现。

方法2

Sequelize:

var note = yield Note.create({'title': 'note'});
var tag = yield Tag.create({'name': 'tag'});
yield note.addTag(tag, {'type': 1});

SQL:

INSERT INTO `notes`
(`id`,`title`,`updated_at`,`created_at`)
VALUES
(DEFAULT,'note','2015-11-06 02:20:52','2015-11-06 02:20:52');

INSERT INTO `tags`
(`id`,`name`,`updated_at`,`created_at`)
VALUES
(DEFAULT,'tag','2015-11-06 02:20:52','2015-11-06 02:20:52');

INSERT INTO `taggings`
(`tag_id`,`note_id`,`type`,`created_at`,`updated_at`)
VALUES
(1,1,1,'2015-11-06 02:20:52','2015-11-06 02:20:52');

这种方法和上面的方法实际上是一样的。只是先手动create了一个Tag模型。

方法3

Sequelize:

var note = yield Note.create({'title': 'note'});
var tag1 = yield Tag.create({'name': 'tag1'});
var tag2 = yield Tag.create({'name': 'tag2'});
yield note.addTags([tag1, tag2], {'type': 2});

SQL:

INSERT INTO `notes`
(`id`,`title`,`updated_at`,`created_at`)
VALUES
(DEFAULT,'note','2015-11-06 02:25:18','2015-11-06 02:25:18');

INSERT INTO `tags`
(`id`,`name`,`updated_at`,`created_at`)
VALUES
(DEFAULT,'tag1','2015-11-06 02:25:18','2015-11-06 02:25:18');

INSERT INTO `tags`
(`id`,`name`,`updated_at`,`created_at`)
VALUES
(DEFAULT,'tag2','2015-11-06 02:25:18','2015-11-06 02:25:18');

INSERT INTO `taggings` (`tag_id`,`note_id`,`type`,`created_at`,`updated_at`)
VALUES
(1,1,2,'2015-11-06 02:25:18','2015-11-06 02:25:18'),
(2,1,2,'2015-11-06 02:25:18','2015-11-06 02:25:18');

这种方法可以进行批量添加。当执行addTags时,实际上就是设置好对应的外键及关系模型本身的属性,然后在关系表中批量的插入数据。



Sequelize:

// 先添加几个tag
var note = yield Note.create({'title': 'note'});
var tag1 = yield Tag.create({'name': 'tag1'});
var tag2 = yield Tag.create({'name': 'tag2'});
yield note.addTags([tag1, tag2], {'type': 2});
// 将tag改掉
var tag3 = yield Tag.create({'name': 'tag3'});
var tag4 = yield Tag.create({'name': 'tag4'});
yield note.setTags([tag3, tag4], {'type': 3});

SQL:

/* 前面添加部分的sql,和上面一样*/
INSERT INTO `notes`
(`id`,`title`,`updated_at`,`created_at`)
VALUES
(DEFAULT,'note','2015-11-06 02:25:18','2015-11-06 02:25:18');

INSERT INTO `tags`
(`id`,`name`,`updated_at`,`created_at`)
VALUES
(DEFAULT,'tag1','2015-11-06 02:25:18','2015-11-06 02:25:18');

INSERT INTO `tags`
(`id`,`name`,`updated_at`,`created_at`)
VALUES
(DEFAULT,'tag2','2015-11-06 02:25:18','2015-11-06 02:25:18');

INSERT INTO `taggings`
(`tag_id`,`note_id`,`type`,`created_at`,`updated_at`)
VALUES
(1,1,2,'2015-11-06 02:25:18','2015-11-06 02:25:18'),
(2,1,2,'2015-11-06 02:25:18','2015-11-06 02:25:18');

/* 更改部分的sql */
INSERT INTO `tags`
(`id`,`name`,`updated_at`,`created_at`)
VALUES
(DEFAULT,'tag3','2015-11-06 02:29:55','2015-11-06 02:29:55');

INSERT INTO `tags`
(`id`,`name`,`updated_at`,`created_at`)
VALUES
(DEFAULT,'tag4','2015-11-06 02:29:55','2015-11-06 02:29:55');

/* 先删除关系 */
DELETE FROM `taggings`
WHERE `note_id` = 1 AND `tag_id` IN (1, 2);

/* 插入新关系 */
INSERT INTO `taggings`
(`tag_id`,`note_id`,`type`,`created_at`,`updated_at`)
VALUES
(3,1,3,'2015-11-06 02:29:55','2015-11-06 02:29:55'),
(4,1,3,'2015-11-06 02:29:55','2015-11-06 02:29:55');

执行逻辑是,先将tag1、tag2在关系表中的关系删除,然后再将tag3、tag4对应的关系插入关系表。



Sequelize:

// 先添加几个tag
var note = yield Note.create({'title': 'note'});
var tag1 = yield Tag.create({'name': 'tag1'});
var tag2 = yield Tag.create({'name': 'tag2'});
var tag3 = yield Tag.create({'name': 'tag2'});
yield note.addTags([tag1, tag2, tag3], {'type': 2});

// 删除一个
yield note.removeTag(tag1);

// 全部删除
yield note.setTags([]);

SQL:

/* 删除一个 */
DELETE FROM `taggings` WHERE `note_id` = 1 AND `tag_id` IN (1);

/* 删除全部 */
SELECT `type`, `created_at`, `updated_at`, `tag_id`, `note_id`
FROM `taggings` AS `tagging`
WHERE `tagging`.`note_id` = 1;

DELETE FROM `taggings` WHERE `note_id` = 1 AND `tag_id` IN (2, 3);

删除一个很简单,直接将关系表中的数据删除。
全部删除时,首先需要查出关系表中note_id对应的所有数据,然后一次删掉。


情况1

查询note所有满足条件的tag。

Sequelize:

var tags = yield note.getTags({
    //这里可以对tags进行where
});
tags.forEach(function(tag) {
    // 关系模型可以通过tag.tagging来访问
    console.log(tag);
});

SQL:

SELECT `tag`.`id`, `tag`.`name`, `tag`.`created_at`, `tag`.`updated_at`,
`tagging`.`type` AS `tagging.type`, `tagging`.`created_at` AS `tagging.created_at`, `tagging`.`updated_at` AS `tagging.updated_at`, `tagging`.`tag_id` AS `tagging.tag_id`, `tagging`.`note_id` AS `tagging.note_id`
FROM `tags` AS `tag`
INNER JOIN `taggings` AS `tagging`
ON
`tag`.`id` = `tagging`.`tag_id` AND `tagging`.`note_id` = 1;

可以看到这种查询,就是执行一个inner join。

情况2

查询所有满足条件的tag,同时获取每个tag所在的note。

Sequelize:

var tags = yield Tag.findAll({
    'include': [
        {
            'model': Note
            // 这里可以对notes进行where
        }
    ]
    // 这里可以对tags进行where
});
tags.forEach(function(tag) {
    // tag的notes可以通过tag.notes访问,关系模型可以通过tag.notes[0].tagging访问
    console.log(tag);
});

SQL:

SELECT `tag`.`id`, `tag`.`name`, `tag`.`created_at`, `tag`.`updated_at`,
`notes`.`id` AS `notes.id`, `notes`.`title` AS `notes.title`, `notes`.`created_at` AS `notes.created_at`, `notes`.`updated_at` AS `notes.updated_at`,
`notes.tagging`.`type` AS `notes.tagging.type`, `notes.tagging`.`created_at` AS `notes.tagging.created_at`, `notes.tagging`.`updated_at` AS `notes.tagging.updated_at`, `notes.tagging`.`tag_id` AS `notes.tagging.tag_id`, `notes.tagging`.`note_id` AS `notes.tagging.note_id`
FROM `tags` AS `tag`
LEFT OUTER JOIN
(
    `taggings` AS `notes.tagging` INNER JOIN `notes` AS `notes`
    ON
    `notes`.`id` = `notes.tagging`.`note_id`
)
ON `tag`.`id` = `notes.tagging`.`tag_id`;

首先是notes和taggings进行了一个inner join,选出notes;
然后tags和刚join出的集合再做一次left join,得到结果。

情况3

查询所有满足条件的note,同时获取每个note所有满足条件的tag。

Sequelize:

var notes = yield Note.findAll({
    'include': [
        {
            'model': Tag
            // 这里可以对tags进行where
        }
    ]
    // 这里可以对notes进行where
});
notes.forEach(function(note) {
    // note的tags可以通过note.tags访问,关系模型通过note.tags[0].tagging访问
    console.log(note);
});

SQL:

SELECT
`note`.`id`, `note`.`title`, `note`.`created_at`, `note`.`updated_at`,
`tags`.`id` AS `tags.id`, `tags`.`name` AS `tags.name`, `tags`.`created_at` AS `tags.created_at`, `tags`.`updated_at` AS `tags.updated_at`,
`tags.tagging`.`type` AS `tags.tagging.type`, `tags.tagging`.`created_at` AS `tags.tagging.created_at`, `tags.tagging`.`updated_at` AS `tags.tagging.updated_at`, `tags.tagging`.`tag_id` AS `tags.tagging.tag_id`, `tags.tagging`.`note_id` AS `tags.tagging.note_id`
FROM `notes` AS `note`
LEFT OUTER JOIN
(
    `taggings` AS `tags.tagging` INNER JOIN `tags` AS `tags`
    ON
    `tags`.`id` = `tags.tagging`.`tag_id`
)
ON
`note`.`id` = `tags.tagging`.`note_id`;

这个查询和上面的查询类似。
首先是tags和taggins进行了一个inner join,选出tags;
然后notes和刚join出的集合再做一次left join,得到结果。





posted @ 2016-06-22 15:11  晴明桑  阅读(1872)  评论(0编辑  收藏  举报