node.js+mysql实现分库分表存查数据:

node.js+mysql实现分库分表:

1.分库分表使用场景:

互联网项目中常用到的关系型数据库,如MySQL,随着用户和业务的增长,传统的单库单表模式难以满足大量的业务数据存储以及查询,单库单表中大量的数据会使写入、查询效率非常之慢,此时可以采取分库分表策略来解决,下面将以仪表采集数据项目为例来介绍。

2.业务需求:

手上有一个项目,需要按秒或按分钟采集数据存到数据库中,不同的用户有N多台不同的仪表,每个仪表有很多基础的配置信息和采集的数据,此时要分库分表(按年分库)存数据来优化性能。

3.分表方式(水平分表和垂直分表):

水平分表: 按数据量的范围对数据进行分表存储,例如:11000000为一个表,10000012000000为一个表,一次类推。

优点: 扩容简单,提前建好库、表就好;

缺点:操作数据会有IO瓶颈,单表热点访问量过大问题;

针对这种问题可以使用HASH取模的方式将数据平均的落到每一个表中(仪表id取模表的数据得到的余数作为实时采集表的后缀如:data_0表、data_1表)

HASH取模方法优点: 能保证数据较均匀的分散落在不同的库、表中,减轻了数据库表压力

HASH取模方法缺点: 扩容麻烦、迁移数据时每次都需要重新计算hash值分配到不同的库和表

垂直分表:指将某个表中某些业务数据分到多个表中,如:之前的用户订单表拆分为用户表和订单表。

4.代码演示:

实际项目中可根据项目业务结合两种分表方法使用,按年分库按数量分表,具体如下:

1.对递增幅度比较大的表进行水平拆分: 这种方法需要估算单表在不分表的情况下大概数据,而mysql单表数据量推荐300万条以下,具体还要看每条数据所占的字节数。比如一年的数据量有1亿条,此时按每个表存100W条计算,那么需要分表数量为:100000000 / 1000000 = 10,此时我们可以提前将一年的数据表建好,表名可以使用前缀加后缀索引的方式,如:data_0、data_1、…、data_9

2.将数据均匀地落到对应的表中: 上面的表已经分好了,此时需要将仪表的数据存到对应的表中,每一个表都有一个唯一的id,当然你的项目可能不是仪表,比如某个用户也是有唯一id的,此时可以用仪表的id 取模 表的数量,得到的余数就是要落入到对应表的后缀,如:仪表id为9,那么数据应该落到:9%10 = 9 ,即:data_9, 注意:查某个仪表的数据也是通过仪表id取模表的数量找到对应的表。

3.分库: 上面已经对数据进行了分表处理,那么接下来就是分库处理,需要知道的是,不能使用同一条SQL语句查询多个库中的记录数据(查阅资料大概可以垮库查询,市面上很多程序也不支持垮库,这里我不做过多纠结),因此我们只需要将数据库有规律的命名查询即可,这里推荐按年份分库,如:platformdatadb2022、platformdatadb2023、platformdatadb2024,需要注意的是每个库中应提前建好data_索引表,此时我们写入和读数据只需要切换到对应的库即可。

4.默认库: 项目中应该有个默认数据变化幅度比较小的库,我们此时可以在这个默认的库上使用切换库查询,数据写入应该每年切换到当前年所在的库,而切换库时应该确保库的存在,此时查询当前年库是否存在也是需要在默认库的基础上查询的,默认库配置如下:

// 引入mysql数据库模块
const mysql = require('mysql')
// 默认数据库配置:
let mysqlHost = {host : 'localhost',user : 'root',database : 'platf***defaultdb',password : '*********',timezone: "08:00",connectionLimit: 2
}
const connection = mysql.createConnection(mysqlHost)
//导出默认数据库模块:
module.exports = connection

5.定时事务: 了解上面后,你可能会有个问题,那么谁每年去创建存大数量的数据库呢?此时你可能想到定时器,但是想想简单的定时器也不能满足要求,因为简单的定时器是隔一段时间执行一次或不断的隔一段时间执行某段代码,那么这个定时器是需要创建的,可能是某个不确定的时间,那么这样就不能控制什么时间去执行某段代码,为解决上面问题,我们可以使用定时事务,所谓定时事务是指,在某个时间点去执行某段代码,比如一天中的某个时刻、一年中的某个时刻,node.js中定时事务我们可以装一个node-schedule包来完成,具体如下(此模块将导出每年自动切换的存大量数据的数据库,其他基础数据应该从默认库中操作):

// 引入mysql数据库模块,供创建新数据库连接
const mysql = require('mysql')
// 导入定时任务模块:
const schedule = require('node-schedule')
// 导入默认数据库:
const connection = require('./mysqldbconfig')// 1.创建数据库方法:将每年创建数据库的业务封装到一个函数中:
function createDataBaseHandle (year) {return new Promise((resolve)=>{// 1.创建库:function createDataBase() {return new Promise((resolve1)=>{let createDataBaceSql = 'CREATE DATABASE platformdatadb'+year+''connection.query(createDataBaceSql,(error)=>{try {if (error) {throw error} else {console.log(year+'年数据库已创建,开始创建报警表:')resolve1()}} catch (err) {console.log('1.创建数据库错误:'+err)}})})}// 2.创建报警表:async function createWarnTable () {// 创建数据库完成:await createDataBase()return new Promise((resolve2)=>{let createWarnTableSql = 'CREATE TABLE platformdatadb'+year+'.`sms_record`( `sms_id` INT NOT NULL AUTO_INCREMENT, `subtime` TIMESTAMP NOT NULL, `equipment_id` INT NOT NULL, `user_id` INT NOT NULL, `content` VARCHAR(255), `sxdx` INT NOT NULL, `number` VARCHAR(255), `sffcg` VARCHAR(255) NOT NULL, `tongji` INT DEFAULT 0, PRIMARY KEY (`sms_id`) ); 'connection.query(createWarnTableSql,(error)=>{try {if (error) {throw error} else {console.log('创建报警表已完成,开始创建实时值表:')resolve2()}} catch (err) {console.log('2.创建报警表错误:'+err)}})})}// 3.创建订单表:async function createOrderTable () {// 创建报警表完成await createWarnTable()return new Promise((resolve) => {let createOrderTableSql = 'CREATE TABLE platformdatadb'+year+'.`recharge_order`(`id` INT NOT NULL AUTO_INCREMENT, `order_private` VARCHAR(20) NOT NULL, `uid` INT NOT NULL, `pid` INT NOT NULL, `total_money` DECIMAL(19,2) NOT NULL, `create_time` TIMESTAMP NOT NULL, `mark` VARCHAR(100), `action_time` TIMESTAMP, `state` INT NOT NULL DEFAULT 0, `pay_time` TIMESTAMP NOT NULL, PRIMARY KEY (`id`) ); 'defaultConnection.query(createOrderTableSql,(error)=>{try {if (error) {throw error} else {console.log('订单表创建已完成,开始创建实时值表:')resolve()}} catch (err) {console.log('创建订单表错误:'+err)}})})}// 4.创建实时值表:async function createDataTable () {// 创建订单表完成await createOrderTable()// 递归的方式创建实时值表:return new Promise((resolve3)=>{let i = 0function start() {if (i > 999) {console.log('实时值表创建完成,开始创建累计值表:')resolve3()return} else {console.log('正在创建表:data_'+i)}let createDataTableSql = 'CREATE TABLE platformdatadb'+year+'.`data_'+i+'`( `id` INT NOT NULL AUTO_INCREMENT,`pid` INT NOT NULL, `D1` VARCHAR(50), `D2` VARCHAR(50), `D3` VARCHAR(50), `D4` VARCHAR(50), `D5` VARCHAR(50), `D6` VARCHAR(50), `D7` VARCHAR(50), `D8` VARCHAR(50), `D9` VARCHAR(50), `D10` VARCHAR(50), `D11` VARCHAR(50), `D12` VARCHAR(50), `D13` VARCHAR(50), `D14` VARCHAR(50), `D15` VARCHAR(50), `D16` VARCHAR(50), `D17` VARCHAR(50), `D18` VARCHAR(50), `D19` VARCHAR(50), `D20` VARCHAR(50), `D21` VARCHAR(50), `D22` VARCHAR(50), `D23` VARCHAR(50), `D24` VARCHAR(50), `D25` VARCHAR(50), `D26` VARCHAR(50), `D27` VARCHAR(50), `D28` VARCHAR(50), `D29` VARCHAR(50), `D30` VARCHAR(50), `D31` VARCHAR(50), `D32` VARCHAR(50), `D33` VARCHAR(50), `D34` VARCHAR(50), `D35` VARCHAR(50), `D36` VARCHAR(50), `D37` VARCHAR(50), `D38` VARCHAR(50), `D39` VARCHAR(50), `D40` VARCHAR(50), `D41` VARCHAR(50), `D42` VARCHAR(50), `D43` VARCHAR(50), `D44` VARCHAR(50), `D45` VARCHAR(50), `D46` VARCHAR(50), `D47` VARCHAR(50), `D48` VARCHAR(50), `D49` VARCHAR(50), `D50` VARCHAR(50) ,`subTime` TIMESTAMP NOT NULL, PRIMARY KEY (`id`) )'connection.query(createDataTableSql,(error)=>{try {if (error) {throw error} else {i++start()}} catch (err)  {console.log('3创建实时值表错误:'+err)}})}start()})}// 5. 创建累计值表async function createCumulativeTable () {// 创建实时值表完成await createDataTable()// 递归的方式创建实时值表:let i = 0function start() {if (i > 999) {console.log('数据库和表创建已完成!')resolve()return} else {console.log('正在创建表:cumulative_'+i)}let createDataTableSql = 'CREATE TABLE platformdatadb'+year+'.`cumulative_'+i+'`( `id` INT NOT NULL AUTO_INCREMENT,`pid` INT NOT NULL, `accum1` VARCHAR(50), `accum2` VARCHAR(50), `accum3` VARCHAR(50), `accum4` VARCHAR(50), `accum5` VARCHAR(50), `accum6` VARCHAR(50), `accum7` VARCHAR(50), `accum8` VARCHAR(50), `accum9` VARCHAR(50), `accum10` VARCHAR(50), `accum11` VARCHAR(50), `accum12` VARCHAR(50), `accum13` VARCHAR(50), `accum14` VARCHAR(50), `accum15` VARCHAR(50), `accum16` VARCHAR(50), `accum17` VARCHAR(50), `accum18` VARCHAR(50), `accum19` VARCHAR(50), `accum20` VARCHAR(50), `accum21` VARCHAR(50), `accum22` VARCHAR(50), `accum23` VARCHAR(50), `accum24` VARCHAR(50), `accum25` VARCHAR(50), `accum26` VARCHAR(50), `accum27` VARCHAR(50), `accum28` VARCHAR(50), `accum29` VARCHAR(50), `accum30` VARCHAR(50), `accum31` VARCHAR(50), `accum32` VARCHAR(50), `accum33` VARCHAR(50), `accum34` VARCHAR(50), `accum35` VARCHAR(50), `accum36` VARCHAR(50), `accum37` VARCHAR(50), `accum38` VARCHAR(50), `accum39` VARCHAR(50), `accum40` VARCHAR(50), `accum41` VARCHAR(50), `accum42` VARCHAR(50), `accum43` VARCHAR(50), `accum44` VARCHAR(50), `accum45` VARCHAR(50), `accum46` VARCHAR(50), `D47` VARCHAR(50), `accum48` VARCHAR(50), `accum49` VARCHAR(50), `accum50` VARCHAR(50) ,`subtime` TIMESTAMP NOT NULL, PRIMARY KEY (`id`) )'connection.query(createDataTableSql,(error)=>{try {if (error) {throw error} else {i++start()}} catch (err) {console.log('4.创建实时值表错误:'+err)}})}start()}createCumulativeTable()})
}// 2.每年12月1日自动创建下一年数据库:(利用定时事务每年的12月1日0点0时0分开始创建下一年数据库)
schedule.scheduleJob('0 0 0 1 12 *', async function () {let date = new Date()let yearstr = date.getFullYear()// 调用创建数据库方法创建数据库:let nextYear = parseInt(yearstr) + 1console.log('自动开始创建'+nextYear+'年数据库')await createDataBaseHandle(nextYear)
})// 3.创建动态数据库配置(首次启动程序连接当前年份数据库,下一年自动切换到新一年数据库):
function createMysqlObj() {let date = new Date()let yearstr = date.getFullYear()// 动态数据库配置:let mysqlHost = {host : 'localhost',user : 'root',database : 'platf***datadb' + yearstr,password : '*********',timezone: "08:00",connectionLimit: 100}return mysqlHost
}// 4.程序每次启动获取当前时间并切换到当前时间数据库:
let mysqlConfig = createMysqlObj()// 5.每年1月1日自动切换到最新数据库(当前年的数据库):
schedule.scheduleJob('0 0 0 1 1 *', function () {let date = new Date()let yearstr = date.getFullYear()// 如果新的一年数据库创建了那么立马切换到新创建的数据库,否则还是沿用旧数据库let sql = 'SELECT table_name FROM `INFORMATION_SCHEMA`.`TABLES` WHERE table_schema = platformdatadb'+yearstrconnection.query(sql,async (error,result)=>{try {if (error) {throw error} else {if (result.length !== 0) {mysqlConfig.database = 'platformdatadb' + yearstr} else {// 如果发现新的数据库没有创建,那么自动创建数据库,创建完后再切换到新的数据库:(此问题一般不会出现,除非在创建数据库的时候服务器突然掉线,这种概率很小的事情即使发生,我们也可以手动创建库,手动创建的代码我会粘在后面)await createDataBaseHandle(yearstr)mysqlConfig.database = 'platformdatadb' + yearstr}}} catch (err) {console.log('查询新创建数据库是否存在失败')}})
})const dataConnection = mysql.createConnection(mysqlConfig)
//导出动态按年切换数据库模块:
module.exports = dataConnection

6.接口中使用默认库和动态库:

创建完上面默认库和动态库后,你可能还是不太会,那么下面我将我项目中某个接口代码粘到下面供大家参考,下面是我历史查询数据业务的接口代码(因为之前按年分库,如果时间段跨年此时就会有查不到的数据,我在这里做出提示,时间段不能跨年,不能跨年指同一时间段不能有两个年份出现,单个年份是可以查到数据的!)

const express = require('express')
const router = express.Router()
// 引入默认数据库,用来查基础数据
const connection = require('../config/mysqldbconfig')
// 引入存数据的动态数据库配置:
const dataConnection = require('../config/mysqldbdataconfig')
//引入token工具
const {verifyToken} = require('../commethods/creattoken')// 根据时间进行查询历史数据的接口:
router.post('/api/getdailycount',(request,response)=>{let {equipId,selectday,type} = request.bodyconst {authorization} = request.headerslet isOk = verifyToken(authorization)isOk.then(() => {// 当selectday为空字符或空数组时返回{cod:201,msg:'超出查询范围,数据库不存在!'}if (selectday == '' || selectday == []) {response.send({cod:201,msg:'超出查询范围,数据库不存在!'})return}// 通道名称容器:let arrnam = []// 仪表id取模表的数量得到要查询的表后缀索引(我项目中表data_表有1000个)let tableIndex = equipId % 1000// 查询要显示通道数量和通道名称:function queryCount() {let sql = 'SELECT qp.equipment_d, cn.* FROM `equipment` qp INNER JOIN `channe_name` cn ON cn.pid = "'+equipId+'" WHERE qp.id = "'+equipId+'" LIMIT 1'return new Promise((resolve)=>{// 从默认库中查基础数据:connection.query(sql,(error,result) => {try {if (error) {throw error} else {if (result.length !== 0) {let counts = result[0].equipment_dfor (let i = 1; i <= counts -2; i++) {//  遍历对象拿到通道名称:for (let key in result[0]){if(key === ('named'+i)){// 将通道名称和绑定的D值存到对象中arrnam.push({nam:result[0][key],dat:'accum'+i})}}}resolve(result)} else {response.send({cod:201,msg:'查询历史仪表名称数据失败'})}}} catch(err){console.log('查询设备通道名称和显示通道数错误:'+err)}})})}// 动态库中查统计值async function selectdata(){let countAndName = await queryCount()// 生成要查询的通道值字段:let counts = parseInt(countAndName[0].equipment_d) - 2let nameArray = []for (let i = 1; i <= counts; i++) {nameArray.push('accum'+i)}let dStr = nameArray.toString()// 判断查询时间是否为当前年份,如果是的话,直接查当前时间默认数据库,否则以输入时间为根据查对应的时间数据库let startYear = selectday.slice(0,4)let nowYear = new Date().getFullYear()// 要查询数据的sql语句let sql = ''// 判断查询类型切换到对应的sql:if (type === 'daily') {if (startYear == nowYear) {// 判断查询时间是否为当前年份,如果不是则从对应的数据库中查询数据sql = 'SELECT '+dStr+', subtime FROM cumulative_'+tableIndex+' WHERE pid = '+equipId+' AND DATE_FORMAT(subtime,"\%Y-\%m-\%d") ="'+selectday+'" ORDER BY id DESC'} else {sql = 'SELECT '+dStr+', subtime FROM platformdatadb'+startYear+'.cumulative_'+tableIndex+' WHERE pid ='+equipId+' AND DATE_FORMAT(`subtime`,"\%Y-\%m-\%d") ="'+selectday+'" ORDER BY id DESC'}}if (type === 'monthly') {if (startYear == nowYear) {// 判断查询时间是否为当前年份,如果不是则从对应的数据库中查询数据sql = 'SELECT '+dStr+', subtime FROM cumulative_'+tableIndex+' WHERE pid = '+equipId+' AND DATE_FORMAT(subtime,"\%Y-\%m") ="'+selectday+'" ORDER BY id DESC'} else {sql = 'SELECT '+dStr+',subtime FROM platformdatadb'+startYear+'.cumulative_'+tableIndex+' WHERE pid ='+equipId+' AND DATE_FORMAT(`subtime`,"\%Y-\%m") ="'+selectday+'" ORDER BY id DESC'}}if (type === 'yearly') {if (startYear == nowYear) {// 判断查询时间是否为当前年份,如果不是则从对应的数据库中查询数据sql = 'SELECT '+dStr+', subtime FROM cumulative_'+tableIndex+' WHERE pid = '+equipId+' AND DATE_FORMAT(subtime,"\%Y") ="'+selectday+'" ORDER BY id DESC'} else {sql = 'SELECT '+dStr+',subtime FROM platformdatadb'+startYear+'.cumulative_'+tableIndex+' WHERE pid ='+equipId+' AND DATE_FORMAT(`subtime`,"\%Y") ="'+selectday+'" ORDER BY id DESC'}}if (type === 'poly' || type === 'caculdate') {const startYear = selectday[0].slice(0,4)const endYear = selectday[1].slice(0,4)if (startYear == endYear) {let nowYear = new Date().getFullYear()if (nowYear == startYear) {// 判断查询时间是否为当前年份,如果不是则从对应的数据库中查询数据sql = 'SELECT '+dStr+', subtime FROM cumulative_'+tableIndex+' WHERE pid = '+equipId+' AND `subtime` >= "'+selectday[0]+'" AND `subtime` <= "'+selectday[1]+'" ORDER BY id DESC'} else {sql = 'SELECT '+dStr+',subtime FROM platformdatadb'+startYear+'.cumulative_'+tableIndex+' WHERE pid ='+equipId+' AND `subtime` >= "'+selectday[0]+'" AND `subtime` <= "'+selectday[1]+'" ORDER BY id DESC'}}}dataConnection.query(sql,(error,result) => {try {if (error) {throw error} else {// 前端历史曲线和表格都是用的同一批数据,这里将名称和值分开返回,便于前端处理成对应的格式response.send({cod:200,msg:'请求数据成功', dataArray: result, nameArray: arrnam})}}catch(err){response.send({cod:201,msg:'超出查询范围,数据库不存在!'})console.log('根据时间进行查询统计值接口错误:'+err)}})}selectdata()}).catch(()=>{response.send({cod:201,msg:'查询历史数据失败'})})
})module.exports = router

7.手动创建数据库:

手动创建数据库其实很简单,但是对于非程序员或非此项目开发者可能就不是很好搞了,那么在这里我写了个基于node环境的脚本,只需简单两步就可以自动创建数据库(实际是将创建数据库的方法抽离出来放到单独的文件通过执行sql语句使用网络方式创建库,通过node命令执行此文件中createDataBaseHandle函数即可,执行时一定要传入参数),代码如下:

// 引入默认数据库配置:
const defaultConnection = require('./config/mysqldbconfig')
// 创建数据库:
// 1.创建数据库方法:
function createDataBaseHandle (year) {// 1.创建库:function createDataBase() {return new Promise((resolve)=>{let createDataBaceSql = 'CREATE DATABASE platformdatadb'+year+''defaultConnection.query(createDataBaceSql,(error)=>{try {if (error) {throw error} else {console.log(year+'年数据库已创建,开始创建报警表:')resolve()}} catch (err) {console.log('1.创建数据库错误:'+err)}})})}// 2.创建报警表:async function createWarnTable () {// 创建数据库完成:await createDataBase()return new Promise((resolve)=>{let createWarnTableSql = 'CREATE TABLE platformdatadb'+year+'.`sms_record`( `sms_id` INT NOT NULL AUTO_INCREMENT, `subtime` TIMESTAMP NOT NULL, `equipment_id` INT NOT NULL, `user_id` INT NOT NULL, `content` VARCHAR(255), `sxdx` INT NOT NULL, `number` VARCHAR(255), `sffcg` VARCHAR(255) NOT NULL, `tongji` INT DEFAULT 0, PRIMARY KEY (`sms_id`) ); 'defaultConnection.query(createWarnTableSql,(error)=>{try {if (error) {throw error} else {console.log('创建报警表已完成,开始创建订单表:')resolve()}} catch (err) {console.log('创建报警表错误:'+err)}})})}// 3.创建订单表:async function createOrderTable () {// 创建报警表完成await createWarnTable()return new Promise((resolve) => {let createOrderTableSql = 'CREATE TABLE platformdatadb'+year+'.`recharge_order`(`id` INT NOT NULL AUTO_INCREMENT, `order_private` VARCHAR(20) NOT NULL, `uid` INT NOT NULL, `pid` INT NOT NULL, `total_money` DECIMAL(19,2) NOT NULL, `create_time` TIMESTAMP NOT NULL, `mark` VARCHAR(100), `action_time` TIMESTAMP, `state` INT NOT NULL DEFAULT 0, `pay_time` TIMESTAMP NOT NULL, PRIMARY KEY (`id`) ); 'defaultConnection.query(createOrderTableSql,(error)=>{try {if (error) {throw error} else {console.log('订单表创建已完成,开始创建实时值表:')resolve()}} catch (err) {console.log('创建订单表错误:'+err)}})})}// 4.创建实时值表:async function createDataTable () {// 创建订单表完成await createOrderTable()// 递归的方式创建实时值表:return new Promise((resolve)=>{let i = 0function start() {if (i > 999) {console.log('实时值表创建完成,开始创建累计值表:')resolve()return} else {console.log('正在创建表:data_'+i)}let createDataTableSql = 'CREATE TABLE platformdatadb'+year+'.`data_'+i+'`( `id` INT NOT NULL AUTO_INCREMENT,`pid` INT NOT NULL, `D1` VARCHAR(50), `D2` VARCHAR(50), `D3` VARCHAR(50), `D4` VARCHAR(50), `D5` VARCHAR(50), `D6` VARCHAR(50), `D7` VARCHAR(50), `D8` VARCHAR(50), `D9` VARCHAR(50), `D10` VARCHAR(50), `D11` VARCHAR(50), `D12` VARCHAR(50), `D13` VARCHAR(50), `D14` VARCHAR(50), `D15` VARCHAR(50), `D16` VARCHAR(50), `D17` VARCHAR(50), `D18` VARCHAR(50), `D19` VARCHAR(50), `D20` VARCHAR(50), `D21` VARCHAR(50), `D22` VARCHAR(50), `D23` VARCHAR(50), `D24` VARCHAR(50), `D25` VARCHAR(50), `D26` VARCHAR(50), `D27` VARCHAR(50), `D28` VARCHAR(50), `D29` VARCHAR(50), `D30` VARCHAR(50), `D31` VARCHAR(50), `D32` VARCHAR(50), `D33` VARCHAR(50), `D34` VARCHAR(50), `D35` VARCHAR(50), `D36` VARCHAR(50), `D37` VARCHAR(50), `D38` VARCHAR(50), `D39` VARCHAR(50), `D40` VARCHAR(50), `D41` VARCHAR(50), `D42` VARCHAR(50), `D43` VARCHAR(50), `D44` VARCHAR(50), `D45` VARCHAR(50), `D46` VARCHAR(50), `D47` VARCHAR(50), `D48` VARCHAR(50), `D49` VARCHAR(50), `D50` VARCHAR(50) ,`subTime` TIMESTAMP NOT NULL, PRIMARY KEY (`id`) )'defaultConnection.query(createDataTableSql,(error)=>{try {if (error) {throw error} else {i++start()}} catch (err) {console.log('3创建实时值表错误:'+err)}})}start()})}// 5. 创建累计值表async function createCumulativeTable () {// 创建实时值表完成await createDataTable()// 递归的方式创建实时值表:let i = 0function start() {if (i > 999) {console.log('数据库和表创建已完成,按Ctrl + C,选Y退出!')return} else {console.log('正在创建表:cumulative_'+i)}let createDataTableSql = 'CREATE TABLE platformdatadb'+year+'.`cumulative_'+i+'`( `id` INT NOT NULL AUTO_INCREMENT,`pid` INT NOT NULL, `accum1` VARCHAR(50), `accum2` VARCHAR(50), `accum3` VARCHAR(50), `accum4` VARCHAR(50), `accum5` VARCHAR(50), `accum6` VARCHAR(50), `accum7` VARCHAR(50), `accum8` VARCHAR(50), `accum9` VARCHAR(50), `accum10` VARCHAR(50), `accum11` VARCHAR(50), `accum12` VARCHAR(50), `accum13` VARCHAR(50), `accum14` VARCHAR(50), `accum15` VARCHAR(50), `accum16` VARCHAR(50), `accum17` VARCHAR(50), `accum18` VARCHAR(50), `accum19` VARCHAR(50), `accum20` VARCHAR(50), `accum21` VARCHAR(50), `accum22` VARCHAR(50), `accum23` VARCHAR(50), `accum24` VARCHAR(50), `accum25` VARCHAR(50), `accum26` VARCHAR(50), `accum27` VARCHAR(50), `accum28` VARCHAR(50), `accum29` VARCHAR(50), `accum30` VARCHAR(50), `accum31` VARCHAR(50), `accum32` VARCHAR(50), `accum33` VARCHAR(50), `accum34` VARCHAR(50), `accum35` VARCHAR(50), `accum36` VARCHAR(50), `accum37` VARCHAR(50), `accum38` VARCHAR(50), `accum39` VARCHAR(50), `accum40` VARCHAR(50), `accum41` VARCHAR(50), `accum42` VARCHAR(50), `accum43` VARCHAR(50), `accum44` VARCHAR(50), `accum45` VARCHAR(50), `accum46` VARCHAR(50), `accum47` VARCHAR(50), `accum48` VARCHAR(50), `accum49` VARCHAR(50), `accum50` VARCHAR(50) ,`subtime` TIMESTAMP NOT NULL, PRIMARY KEY (`id`) )'defaultConnection.query(createDataTableSql,(error)=>{try {if (error) {throw error} else {i++start()}} catch (err) {console.log('4.创建实时值表错误:'+err)}})}start()}createCumulativeTable()
}// 开启键盘事件:
const readline = require('readline')
const readLine = readline.createInterface({input:process.stdin,output:process.stdout
})
readLine.question('请输入年份,格式为:2022\r\n',(yearStr) => {let yearReg = /^(1949|19[5-9]\d|20\d{2}|2100)$/if (yearReg.test(yearStr)) {createDataBaseHandle(yearStr)} else {console.log('输入年份不对')} readLine.close()
})

在node环境中手动输入命令执行createDataBaseHandle函数创建数据库可能还是不太友好,因此我继续优化,将node环境执行createDataBaseHandle函数的命令放到bat脚本中,那么此时你可以通过双击bat文件,紧接着输入要创建库的年份回车即可自动创建数据库,createDbByHandStarter.bat文件代码如下(.bat文件实则为DOS命令,在windows和linux都可以支持,在mac上面不能双击执行,此时你可以用mac的shell执行此命令):

  node createDbByHandScript.js

提示:本文图片等素材来源于网络,若有侵权,请发邮件至邮箱:810665436@qq.com联系笔者 删除。

笔者:苦海

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/575092.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux 添加新硬盘

1.识别分区和硬盘 在 /dev/ 目录下找到新的硬盘&#xff0c;sda 为本地硬盘&#xff0c;sda1、sda2.. 为分区&#xff0c;sdb 就是新添加的硬盘&#xff0c;如: [rootwusuyuan ~]# ls -ltr /dev/sd*brw-rw----. 1 root disk 8, 0 11月 14 14:12 /dev/sdabrw-rw----. 1 root di…

mybatis 映射成多个list_SSM:Mybatis架构与原理

MyBatis功能架构设计功能架构讲解&#xff1a;我们把Mybatis的功能架构分为三层&#xff1a;API接口层&#xff1a;提供给外部使用的接口API&#xff0c;开发人员通过这些本地API来操纵数据库。接口层一接收到调用请求就会调用数据处理层来完成具体的数据处理。数据处理层&…

10无法勾选隐藏的项目_Excel超好用的隐藏操作,不可多得

日常工作中&#xff0c;表格数据的展示与隐藏也是有大学问的&#xff0c;为了表格更加简洁明了、美观大气&#xff0c;实用的隐藏技巧就派上用场了。且听小翼慢慢分享~1、隐藏行列方法1&#xff1a;选中目标区域&#xff0c;右击鼠标选择“隐藏”即可。如图&#xff1a;方法2&a…

python程序题斐波那契数列_Python编程题9--斐波那契数列

题目1 已知一个数列&#xff1a;1、1、2、3、5、8、13、……&#xff0c;其规律为从第3项开始&#xff0c;每一项都等于其前两项的和&#xff0c;这个数列就是斐波那契数列。 请求出符合斐波那契数列规律的第11项。 代码实现--非递归 def fib_show(n): a, b 0, 1 while n >…

cocos2d-x,求世界坐标

老版: http://user.qzone.qq.com/350479720/blog/1384483239 一&#xff0c;求node的世界坐标。因为node的contentSize为0&#xff0c;局部坐标原点与node重合。所以求起来简单。下面方法都对&#xff1a;1&#xff0c;node->getParent()->convertToWorldSpace(node->…

pdf会签_跟我们做流程管理.pdf

跟我们做流程管理跟我们做流程管理——向管理要效益讲师简介陈立云 先生• AMT 高级咨询经理& 专家讲师• 暨南大学管理学院MBA教育中心校外导师• 著有畅销书 《跟我们做流程管理》&#xff0c;得到用户广泛好评• 曾先后在华为技术、美的电器、佳杰科技担任流程管理专家/…

python爬虫知乎图片_python爬虫(爬取知乎答案图片)

python爬虫&#xff08;爬取知乎答案图片&#xff09; 1.⾸先&#xff0c;你要在电脑⾥安装 python 的环境 我会提供2.7和3.6两个版本的代码,但是本⽂只以python3.6版本为例。 安装完成后&#xff0c;打开你电脑的终端&#xff08;Terminal&#xff09;执⾏以下命令&#xff1a…

HTML5简略介绍

今天要说下 HTML5特有的一个元素 canvas &#xff0c;旨在让web页面上作矢量图不需要在依靠flash或是其他插件&#xff0c;在网页上使用canvas元素时&#xff0c;它会创建一块矩形区域&#xff0c;默认300*150&#xff0c;当然也是可以自定义的。Canvas中的坐标是从左上角开始的…

我的世界java和基岩版哪个好玩_我的世界:Java版本好玩还是基岩版好玩?老玩家看完后沉默了...

MC刚开始是在国外风靡了起来&#xff0c;传到中国的时候MC已经火了很久了&#xff0c;这时候外国已经出现了很多玩MC十分厉害的大神和主播&#xff0c;而在国外也有超多的服务器&#xff0c;其中有号称最强的原版生存服务器Scicraft&#xff0c;而在版本方面MC其实算下来大概有…

SQL Server简介

SQL Server是微软的一款关系型数据库。某些平台吹得天花烂坠&#xff0c;今天第一次在自己的项目中使用了下&#xff0c;感觉不是那么好&#xff0c;特别是SQL语句的支持度还是很欠缺&#xff0c;如limit等都不支持&#xff0c;还有特别单双引号都是需要特别注意的,下面是SQL S…

Windows下断言的类型及实现

一、内容综述 本文主要介绍Windows下断言assert的实现&#xff0c;并总结断言的不同应用准则。最后给出一个windows自定义断言的方法。 本文行文参考《Debugging Windows Programs》第三章相关内容&#xff0c;如果有兴趣的话建议读者可以深入阅读下。 二、断言的类型 1. ANSI…

mysql最大执行时间_导入大型MySQL数据库时,最大执行时间超过300秒

我正在尝试使用命令导入641 MBMySQL数据库&#xff1a;mysql -u root -p ddamiane_fakty < domenyin_damian_fakty.sql但是我收到了一个错误&#xff1a;ERROR 1064 (42000) at line 2351406: You have an error in your SQL syntax; check the manual that corresponds to …

linux右上角不显示网络连接_来体验下Linux吧

在前面的几期中我们从树莓派开始了解Linux&#xff0c;大家可能已经想来试一下手了。趁热打铁&#xff0c;本期我将介绍两种方便体验学习Linux的方法&#xff0c;在线体验或者安装虚拟机。1 在线体验Linux如果想快速的体验下Linux系统&#xff0c;我们可以选择云计算服务商提供…

debian下ror新建项目报错解决

一个是缺少mysql的开发包 sudo apt-get install libmysqld-dev 还有一个报错如下 debian ExecJS::RuntimeUnavailable: Could not find a JavaScript runtime. See https://github.com/sstephenson/execjs for a list of available runtimes. 要么自己下载nodejs的包,要么在gem…

mysql中的默认约束_数据库中默认约束的作用

匿名用户1级2016-04-29 回答数据库约束是为了保证数据的完整性而实现的一套机制&#xff0c;它具体的根据各个不同的数据库的实现而有不同的工具。一般来说有以下几种实现方式&#xff1a;1、检查约束&#xff1a;通过在定义数据库表里&#xff0c;在字段级或者是在表级加入的检…

python md5解密_python 生成文件MD5码

pymd5.py的代码如下&#xff1a; #-*-coding:utf-8-*-Created on 2012-5-25 author: kanpiaoxueimport hashlib import os import sys def printUsage(): print (Usage: [python] pymd5.py ) def createMD5(filePath): if not os.path.isfile(filePath): printUsage() else: tm…

Java数据结构、list集合、ArrayList集合、LinkedList集合、Vector集合

数据结构&#xff1a; 数据存储的常用结构有&#xff1a;栈、队列、数组、链表、红黑树。 栈&#xff1a;stack,又称堆栈&#xff0c;它是运算受限的线性表&#xff0c;其限制是仅允许在标的一端进行插入和删除操作&#xff0c;不允许在其他任何位置进行添加、查找、删除等操…

第11章 GUI Page436 使用缓冲DC, wxBufferedPaintDC

所谓“缓冲DC”&#xff0c;是指将所有图元都先划到一个人眼看不到的“设备上下文”之上&#xff0c;最后再一次性复制到真正的屏幕DC之上&#xff0c;这样我们就看不到中间画的过程了&#xff0c;也就不会感到闪烁了。 注意&#xff0c;这时不能解除ScrolledWindow1的背景擦除…

Tomcat+JSP经典配置实例

经常看到jsp的初学者问tomcat下如何配置jsp、servlet和bean的问题&#xff0c;于是总结了一下如何tomcat下配置jsp、servlet和ben&#xff0c;希望对那些初学者有所帮助。    一、开发环境配置    第一步&#xff1a;下载j2sdk和tomcat&#xff1a;到sun官方站&#xf…

python海龟编辑器画小汽车_【海龟编辑器下载】海龟编辑器(Python编辑器) v1.3.4 官方免费版-趣致软件园...

海龟编辑器是一款专为广大少儿打造的Python编辑器&#xff0c;该软件功能强大且使用便利&#xff0c;可以帮助孩子们以搭积木的方式来学习Python&#xff0c;并且支持Python代码和图形化积木的双向互相转译&#xff0c;不但可以有效提升孩子的学习兴趣&#xff0c;而且还能够降…