赞
踩
SQL Server是微软的一款关系型数据库。某些平台吹得天花烂坠,今天第一次在自己的项目中使用了下,感觉不是那么好,特别是SQL语句的支持度还是很欠缺,如limit等都不支持,还有特别单双引号都是需要特别注意的,下面是SQL Server的安装和使用总结:
SQL Server安装:
1.到官网下载下载器:https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads ,如图:
2.新建一个文件夹并将下载好的下载器移动到这个文件夹中(可以不新建,个人习惯将软件归类管理,但是后面会发现微软的软件很多你是无法很好的管理,因为它会不受控制的安装到C盘去,即使自定义了安装目录,但是还是有很多文件是装到C盘去,吐槽微软!),之后双击下载器后选中基本即可,如:
3.双击基本后会再次弹出一个许可协议,接受后将安装路径修改为自己新建的文件夹,之后点击安装继续,如:
4.看到下面界面表示数据库已经安装成功,此时可以继续安装SSMS(SSMS是一款数据库管理软件,类似SQLyog,但是没有SQLyog好用的数据库管理软件),如:
5.点击完SSMS后会跳到新的网页中去,此时点击如图所示位置即可下载可视化管理软件(如果需要中文,可以继续到下面Available languages点击chinese即可安装中文版),如:
6.将下载好的SSMS下载器放到重新新建的文件夹,之后双击,并修改安装路径后继续点击install进行下载安装,如:
7.安装完成后点击Close即可,点击完CLose后,此时桌面可能没有快捷方式,此时你可以到自己应用中找到名为:azuredatastudio.exe的程序之后双击即可,通过文件所在位置可以看到文件是安装在了C盘,我C盘坚决不安装与系统相关的软件,因此我会将整个文件剪切到其它盘,之后创建快捷方式,打开管理软件如下(类似编辑器,如果想要更可视化的管理数据库,那么还得继续选择其中的工具):
其中的工具如:
**node.js中使用SQL Server: **
node.js中使用SQL Server可以通过第三方包:mssql,类似node.js连接mysql的包mysql一样,我项目中涉及分库分表存储数据,下面我将我项目中主要代码粘贴如下可供参考:
配置默认数据库:默认数据库用来查询项目基础数据,文件命名为:mssqldefaultconfig.js
// 默认数据库配置: // 引入前先: npm i mssql 下载依赖包 const mssql = require('mssql') // 默认数据库配置: const option = { // sql server数据库所在服务器: server: '***.**.***.***', // sql server数据库监听的端口号,默认1433: port: ****, // sql server数据库某个数据库名称: database: '********2021', // 用户名: user: '******', // 密码: password: '*********', options: { encrypt: false, //加密 enableArithAbort: false }, pool: { min: 0, max: 10, idleTimeoutMillis: 3000 } } // 创建默认数据库连接池: const pool = new mssql.ConnectionPool(option) pool.on('error', err => { console.log('数据库连接错误: ', err) }) // 连接数据库: const connection = pool.connect() // 封装一个执行sql语句的方法,用来操作数据库: const defaultDataBaseQuery = async (sql, callBack) => { const ps = new mssql.PreparedStatement(await connection) ps.prepare(sql, err => { if (err){ console.log('prepare错误:' + err) return } ps.execute('', (err, result) => { if (err){ console.log('execute错误:' + err) return } ps.unprepare(err => { if (err){ console.log('unprepare错误:' + err) callBack(err, null) return } callBack(err, result) }) }) }) } // 导出默认数据库: module.exports = defaultDataBaseQuery
配置动态数据库 :动态数据库是指按规定的日期自动切换到某个数据,文件命名为:mssqlswitchconfig.js
// 动态库配置: const mssql = require('mssql') // 定时事务:一个第三方的包,类似定时器,可以设置某段代码多久执行一次(某个日期点执行某段代码) const schedule = require('node-schedule') // 引入默认数据库配置:动态库切换也是需要借助基础库的,因此需要引入基础库: const defaultDataBaseQuery = require('./mssqldefaultconfig') // 导出一个动态的数据库配置:按年份配置数据库 function createSqlServeOption() { let date = new Date() let yearstr = date.getFullYear() // 动态数据库配置: let optionObj = { server: '***.**.***.***', port: ****, // 动态库命名最方便的就是前缀固定+后缀年份等 database: '******' + yearstr, user: '*****', password: '************', options: { encrypt: false, //加密 enableArithAbort: false }, pool: { min: 0, max: 10, idleTimeoutMillis: 3000 } } return optionObj } // 程序启动配置当前年份动态库option: let option = createSqlServeOption() // 数据库连接变量 let pool; // 程序启动连接当前年份动态库: pool = new mssql.ConnectionPool(option) // 每年1月1日自动切换到最新数据库: schedule.scheduleJob('0 0 0 1 1 *', function () { const date = new Date() const yearstr = date.getFullYear() let dbStr = '******' + yearstr // 降低频率切换:因为此项目为老项目二开,数据库中数据和数据结构都是不动的,只提供使用权,这里动态创建新的数据库业务不在我项目,所以我无法创建数据库且不确定新的数据库是否已经创建好,因此只能不断的查询新的数据库是否存在,如果存在立马切换到新的数据库,否则持续每次降低频率的尝试查询数据库是否存在 let count = 0 // 如果新的一年数据库创建了那么立马切换到新创建的数据库,否则延迟递归切换 function switchDB () { // SQL Server中无法使用:'SELECT table_name FROM `INFORMATION_SCHEMA`.`TABLES` WHERE table_schema = ******'+yearstr此sql语句查询某个数据库中是否有某表,只能通过下面数据查询到所有数据库,然后去判断数据库中是否有新的数据库名称 let sql = 'select * From master.dbo.sysdatabases' defaultDataBaseQuery(sql,async (error,result) => { try { if (error) { throw error } else { const isExist = result.recordset.find(item => item.name == dbStr) // 不存在是返回undefined if (isExist) { option.database = dbStr // 动态切换新的数据库连接:如果将new mssql.ConnectionPool(option)提到最外面,这里我不能保证它会重新创建新连接,为了确保期间,我在这里重新创建一次 pool = new mssql.ConnectionPool(option) // 存在的情况下return掉,否则继续延迟尝试切换: return } else { // 延迟重新判断是否存在数据库 setTimeout(() => { count++ switchDB() },1000 * count) } } } catch (err) { console.log('查询新创建数据库是否存在失败' + err) } }) } switchDB() }) // 连接池创建错误时: pool.on('error', err => { console.log('数据库连接错误: ', err) }) // 创建数据库连接: const connection = pool.connect() // 动态库查询方法: const switchDataBaseQuery = async (sql, callBack) => { const ps = new mssql.PreparedStatement(await connection) ps.prepare(sql, err => { if (err){ console.log('prepare错误:' + err) return } ps.execute('', (err, result) => { if (err){ console.log('execute错误:' + err) return } ps.unprepare(err => { if (err){ console.log('unprepare错误:' + err) callBack(err, null) return } callBack(err, result) }) }) }) } // 导出动态数据库: module.exports = switchDataBaseQuery
查询页api接口路由文件 :
// 记录查询页: // 引入日期格式化函数: const {dataFormat} = require('../utils/utils') // 引入路由模块: const Router = require('koa-router') // 引入默认数据库配置 const defaultDataBaseQuery = require('../config/mssqldefaultconfig') // 引入动态数据库配置 const switchDataBaseQuery = require('../config/mssqlswitchconfig') // 引入redis配置文件: const redisClient = require('../config/redisconfig') // 创建路由实例: const router = new Router({prefix:'/api'})//里面可接收一个对象,可以设置些默认配置,如{prefix:'/api'}设置路由匹配前缀 // 在创建路由时默认加了前缀 /api ,下面router.post('/login', ()=>{})可直接匹配:'/api/login'路由 // 查实时值接口: router.post('/getrealtime',async (ctx, next) => { let {id, ParaId} = ctx.request.body // 设备单位等参数redis缓存key let keys = 'equipmentunit' + ParaId return new Promise((resolve,reject) => { // 查仪表相关配置参数: function queryEquipOption() { // 判断是否存在key:存在走缓存,否则走数据库: return new Promise((resolve,reject) => { redisClient.exists(keys, (err, res) => { if (res == 0) { let sql1 = 'SELECT Name,Unit,Xh FROM Equipment_Para_Two WHERE FId= '+ParaId +' AND WeiXin=1' defaultDataBaseQuery(sql1, (error, result) => { if (error) { ctx.body = {cod: 201, msg: '查询错误!'} console.log('查询实时值sql1错误:' + error) reject() } else { if (result.recordset.length === 0) { ctx.body = {cod: 200, msg: '无实时值!', resultList: [], time: dataFormat()} resolve() return } // 将查询到的数据存到redis中600秒过期: redisClient.setex(keys, 60 * 10, JSON.stringify(result)) resolve(result) } }) } else { redisClient.get(keys, (error,res) => { resolve(JSON.parse(res)) }) } }) }) } // 查实时值数据: async function queryTimeData() { const option = await queryEquipOption() // 要获取的通道 const dnArray = [...new Set(option.recordset.map(it => 'D' + it.Xh))] // 要查询的通道值字段: const dnStr = dnArray.toString() // 查询时刻值: let sql2 = 'SELECT TOP 1 '+dnStr+', SubTime FROM Equipment_Data WHERE EqId = '+id+' order by id desc' switchDataBaseQuery(sql2, (error, result) => { if (error) { ctx.body = {cod: 201, msg: '查询错误!'} console.log('查询实时值sql2错误:' + error) } else { if (result.recordset.length === 0) { ctx.body = {cod: 200, msg: '无实时值!', resultList: [], time: dataFormat()} resolve() return } // 实时值数组 let resultArray = [] option.recordset.forEach(item => { resultArray.push({...item,...{value: result.recordset[0]['D' + item['Xh']]}}) }) ctx.body = {cod: 200, msg: '请求实时值数据成功!', resultList: resultArray, time: dataFormat(result.recordset[0].SubTime)} resolve() } }) } queryTimeData() }) }) // 查历史值接口: router.post('/gethistory', async (ctx, next) => { const {date1, date2, ParaId, id, page} = ctx.request.body const startYear = date1.slice(0,4) const endYear = date2.slice(0,4) if (startYear !== endYear) { ctx.body = {cod: 200, msg: '日期不能跨年!', resultList: []} return } // 设备单位等参数redis缓存key let keys = 'equipmentunit' + ParaId return new Promise((resolve, reject) => { // 查仪表相关配置参数: function queryEquipOption() { // 判断是否存在key:存在走缓存,否则走数据库: return new Promise((resolve,reject) => { redisClient.exists(keys, (err, res) => { if (res == 0) { let sql1 = 'SELECT Name,Unit,Xh FROM Equipment_Para_Two WHERE FId= '+ParaId +' AND WeiXin=1' defaultDataBaseQuery(sql1, (error, result) => { if (error) { ctx.body = {cod: 201, msg: '查询错误!'} console.log('查询实时值sql1错误:' + error) reject() } else { if (result.recordset.length === 0) { ctx.body = {cod: 200, msg: '无实时值!', resultList: []} resolve() return } // 将查询到的数据存到redis中600秒过期: redisClient.setex(keys, 60 * 10, JSON.stringify(result)) resolve(result) } }) } else { redisClient.get(keys, (error,res) => { resolve(JSON.parse(res)) }) } }) }) } // 查历史值数据: async function queryHistoryData() { const option = await queryEquipOption() // 要获取的通道 const dnArray = [...new Set(option.recordset.map(it => 'D' + it.Xh))] // 要查询的通道值字段: const dnStr = dnArray.toString() if (page < 1) { ctx.body = {cod: 200, msg: '页数无效!', resultList: []} return } let skip = (page - 1)*10 // 查询历史值: const date = new Date() const currentYear = date.getFullYear() let sql2; // 如果查询日期是当前年份,则执行当前年份动态库,否则切换到对应的历史数据库查询数据: if (startYear == currentYear) { sql2 = "select top 10 "+dnStr+" ,SubTime,EqId from (select row_number() over(order by id desc) as rownumber, "+dnStr+" ,SubTime,EqId from Equipment_Data) temp_row where EqId = "+id+" and SubTime between '"+date1+"' and '"+date2+"' and rownumber > " +skip } else { sql2 = "select top 10 "+dnStr+" ,SubTime,EqId from (select row_number() over(order by id desc) as rownumber, "+dnStr+" ,SubTime,EqId from [YLinK"+startYear+"].[dbo].[Equipment_Data]) temp_row where EqId = "+id+" and SubTime between '"+date1+"' and '"+date2+"' and rownumber > " +skip } switchDataBaseQuery(sql2, (error, result) => { if (error) { ctx.body = {cod: 201, msg: '查询错误!'} console.log('查询历史值sql2错误:' + error) } else { if (result.recordset.length === 0) { ctx.body = {cod: 200, msg: '无历史值!', resultList: []} resolve() return } // 历史值数组 let resultArray = [] // 遍历历史值 result.recordset.forEach(item => { // 二维数组子数组: let arr = [] // 遍历单位数组: option.recordset.forEach(uitem => { // 遍历历史值对象: for (key in item) { if (key.slice(1,2) == uitem.Xh) { arr.push({...uitem,value:item[key]}) } } }) resultArray.push({time: dataFormat(item.SubTime), ValueArray: arr}) }) ctx.body = {cod: 200, msg: '请求历史值数据成功!', resultList: resultArray} resolve() } }) } queryHistoryData() }) }) // 查充值记录接口 router.post('/getpayrec', async (ctx, next) => { const {date1, date2, id, page} = ctx.request.body return new Promise((resolve,reject) => { // 查询充值记录: if (page < 1) { ctx.body = {cod: 200, msg: '页数无效!', resultList: []} resolve() return } let skip = (page - 1) * 10 let sql = "select top 10 EquipmentId,SbTime,No,Amount from (select row_number() over(order by id desc) as rownumber,EquipmentId,SbTime,No,Amount from PayRec) temp_row where EquipmentId = "+id+" and SbTime between '"+date1+"' and '"+date2+"' and rownumber > " +skip defaultDataBaseQuery(sql, (error, result) => { if (error) { ctx.body = {cod: 201, msg: '查询错误!'} console.log('查充值记录sql错误:' + error) } else { result.recordset.map(item => { item.SbTime = dataFormat(item.SbTime) }) ctx.body = {cod: 200, msg: '请求充值记录数据成功!', resultList: result.recordset} resolve() } }) }) }) // 退款记录接口 router.post('/getrefund', async (ctx, next) => { const {date1, date2, id, page} = ctx.request.body return new Promise((resolve,reject) => { // 查询退款记录: if (page < 1) { ctx.body = {cod: 200, msg: '页数无效!', resultList: []} resolve() return } let skip = (page - 1) * 10 let sql = "select top 10 EquipmentId,SbTime,No,Amount from (select row_number() over(order by id desc) as rownumber,EquipmentId,SbTime,No,Amount from Refund) temp_row where EquipmentId = "+id+" and SbTime between '"+date1+"' and '"+date2+"' and rownumber > " +skip defaultDataBaseQuery(sql, (error, result) => { if (error) { ctx.body = {cod: 201, msg: '查询错误!'} console.log('查退款记录sql错误:' + error) } else { result.recordset.map(item => { item.SbTime = dataFormat(item.SbTime) }) ctx.body = {cod: 200, msg: '请求退款记录数据成功!', resultList: result.recordset} resolve() } }) }) }) // 导出路由 module.exports = router
index启动文件 :
// 引入设置响应头的中间件: const setResponseHeader = require('./middelware/setresponseheader') // 引入解析请求体的中间件:koa-bodyparser const bodyParser = require('koa-bodyparser') // 导入查询页路由文件 const recordqueryserve = require('./serves/recordqueryserve') const Koa = require('koa') // 创建Koa实例对象: const app = new Koa() // 挂载设置响应头的中间件: app.use(setResponseHeader) // 挂载koa-bodyparser中间件:如果不配置的话在路由页就不能通过ctx.request.body拿到请求体 app.use(bodyParser()) // 挂载查询页接口路由: app.use(recordqueryserve.routes()) // 绑定端口号: app.listen(3000,() => { console.log('serve is running at 3000!') })
提示:本文图片等素材来源于网络,若有侵权,请发邮件至邮箱:810665436@qq.com联系笔者 删除。
笔者:苦海
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。