• SQL Server简介


    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61

    配置动态数据库 :动态数据库是指按规定的日期自动切换到某个数据,文件命名为: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 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114

    查询页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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245

    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!')
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

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

    笔者:苦海

  • 相关阅读:
    C语言—位运算符
    ​搜狐大量员工遭遇“工资补助”诈骗;​腾讯客服回应“借钱就能避免微信号被封”;TypeScript 4.7发布|极客头条
    Linux 可执行文件瘦身指令 strip 使用示例
    详解SPI
    Redis数据库redisDb源码分析
    【Mybatis小白从0到90%精讲】12:Mybatis删除 delete, 推荐使用主键删除!
    带你学会指针进阶
    动规算法-地下城游戏
    MySQL大数据量高速迁移,500GB只需1个小时
    [附源码]计算机毕业设计JAVA新冠疫苗线上预约系统
  • 原文地址:https://blog.csdn.net/weixin_46758988/article/details/127868308