分区SQL Server函数 Partition By 与 row_number() 、 排序rank()的用法

在 SQL Server 中,PARTITION BY 是用于在窗口函数(如 ROW_NUMBER(), RANK(), DENSE_RANK(), SUM(), AVG(), 等等)中定义分区的一种方法。分区允许你在查询结果集的特定子集中应用窗口函数,而不是在整个结果集上应用。

使用场景
‌1.排名‌:如上面的例子,可以使用 ROW_NUMBER(), RANK(), 或 DENSE_RANK() 来计算分区内的排名。
2‌.累积和/移动平均‌:可以使用 SUM(), AVG() 等聚合函数来计算分区内的累积和或移动平均。
‌3.分组计算‌:可以在每个分区内执行特定的计算,而不需要使用子查询或 GROUP BY。

举个例子,我们先准备好一个学生成绩表的数据源

create table Student  --学生成绩表
(
 id int,  --主键
 Grade int, --班级
 Score int --分数
);

insert into Student values(1,1,88); 
insert into Student values(2,1,66); 
insert into Student values(3,1,75); 
insert into Student values(4,2,30); 
insert into Student values(5,2,70); 
insert into Student values(6,2,80); 
insert into Student values(7,2,60); 
insert into Student values(8,3,90); 
insert into Student values(9,3,70); 
insert into Student values(10,3,80); 
insert into Student values(11,3,80)

一:如果我们想要一个输出每个学生成绩名次的结果,就可以使用row_number() 方法,配合排序

select *,row_number() over(order by Score desc) as Sequence from Student

二:如果我们想要对每个年级的学生的名词分别计算,那就要使用到partition by方法

select *,row_number() over(partition by Grade order by Score desc) as Sequence from Student

接下来,我们还可以利用上述结果进行二次筛选,比如获取每个年级第一名

select * from (
    select *,row_number() over(partition by Grade order by Score desc) as Sequence from Student
  ) T 
where T.Sequence<=1

在这里,我们发现一个现象,当存在一个分数一样的情况,比如三年级有2个80分,但是他们的排名却分了前后,如果要想排名一致,都是第二名,就可以使用rank()方法

三:按照年级,输出每个学生的排名,同分数排名一致

select *,rank() over(partition by Grade order by Score desc) as Sequence from Student;

此时,我们再获取每个年级前2名,就可以取出分数并列第二名的同学

select * from (
    select *,rank() over(partition by Grade order by Score desc) as Sequence from Student
  ) T 
where T.Sequence<=2

注意事项
PARTITION BY 子句中的列应仔细选择,因为它们会影响查询的性能。
如果不需要分区,可以省略 PARTITION BY 子句,此时窗口函数将在整个结果集上应用。
可以结合多个列进行分区,例如 PARTITION BY Grade。
通过使用 PARTITION BY,你可以更灵活和高效地进行数据分析和处理,尤其是在处理大型数据集时。

SQL SERVER批量还原数据库

通常,迁移服务器后,往往需要还原SQLSERVER的备份文件,如果BAK文件一多,恢复起来就比较费时间,以下可以通过SQL脚本的方法,进行批量还原SQLSERVER的备份文件,注意:需要指定 备份文件所在文件夹路径,还有 数据库安装路径中的数据文件夹路径,

1.首先把后缀为bak的备份文件放到指定盘目录上,例如“D:\db\”文件夹

2.找到数据库的安装路径下的数据库目录文件,例如“D:\Software\SQL2014\DB\MSSQL12.MSSQLSERVER\MSSQL\DATA\”

3.修改脚本里对应的目录配置,执行SQL脚本,具体脚本如下

DECLARE @SqlServerPath NVARCHAR(200) --数据库安装路径(数据文件夹)
DECLARE @BackUpPath NVARCHAR(200) --备份文件路径
DECLARE @BackUpFileName NVARCHAR(200) --备份文件名
DECLARE @DbName NVARCHAR(200) --db名称
DECLARE @LogicalDbName NVARCHAR(200) --逻辑db名称
DECLARE @LogicalLogName NVARCHAR(200) --逻辑log名称
DECLARE @BackUpFullFileName NVARCHAR(260) --备份文件全名
DECLARE @RESTORESQL NVARCHAR(2000) --备份数据库完整语句
SET @BackUpPath = 'D:\db\'; --备份文件路径
SET @SqlServerPath = 'D:\Software\SQL2014\DB\MSSQL12.MSSQLSERVER\MSSQL\DATA\'; --数据库安装路径(数据文件夹)
--add path \
if (@BackUpPath IS NOT NULL) AND LEN(@BackUpPath)>1 AND (RIGHT(@BackUpPath,1)<>'\')
BEGIN
 SET @BackUpPath=@BackUpPath+'\'
END
IF OBJECT_ID('tempdb..#Temp') IS NOT NULL
    DROP TABLE #Temp
CREATE TABLE #Temp (id INT IDENTITY,a INT,b INT,c INT)
DECLARE @fpath NVARCHAR(3)
SET @fpath=LEFT(@BackUpPath,3)
INSERT #Temp EXEC master..xp_fileexist @fpath
INSERT #Temp EXEC master..xp_fileexist @BackUpPath
--如果指定盘符有误不存在,则返回错误提示:
IF EXISTS(SELECT 1 FROM #Temp WHERE id=1 AND c=0)
BEGIN
    PRINT CHAR(10) + N'备份文件路径的盘符不存在,请重新输入!'
	GOTO ExitFLag
END
--如果不存在指定的文件夹,则创建:
ELSE IF EXISTS(SELECT 1 FROM #Temp WHERE b=0 AND id=2)
BEGIN
   PRINT CHAR(10) + N'备份文件路径不存在,请重新输入!'
   GOTO ExitFLag
END
IF OBJECT_ID('tempdb..#Dir') IS NOT NULL
    DROP TABLE #Dir
CREATE TABLE #Dir  
(  
     BackDBFileName NVARCHAR(100) ,DEPTH INT ,[File] INT  
)
IF OBJECT_ID('tempdb..#FileListInfo') IS NOT NULL
    DROP TABLE #FileListInfo
CREATE TABLE #FileListInfo
(
	LogicalName              NVARCHAR(128) NULL,
	PhysicalName             NVARCHAR(260) NULL,
	TYPE                     CHAR(1) NULL,
	FileGroupName            NVARCHAR(128) NULL,
	FileSize                 BIGINT NULL,
	MAXSIZE                  BIGINT NULL,
	FileId                   BIGINT,
	CreateLSN                NUMERIC(25, 0),
	DropLSN                  NUMERIC(25, 0) NULL,
	UniqueID                 UNIQUEIDENTIFIER,
	ReadOnlyLSN              NUMERIC(25, 0) NULL,
	ReadWriteLSN             NUMERIC(25, 0) NULL,
	BackupSizeInBytes        BIGINT,
	SourceBlockSize          INT,
	FileGroupID              INT,
	LogGroupGUID             UNIQUEIDENTIFIER NULL,
	DifferentialBaseLSN      NUMERIC(25, 0) NULL,
	DifferentialBaseGUID     UNIQUEIDENTIFIER,
	IsReadOnly               BIT,
	IsPresent                BIT,
	TDEThumbprint            BIT
)
 
INSERT INTO #Dir EXEC master..xp_dirtree @BackUpPath,1,1
 
DELETE FROM #Dir WHERE CHARINDEX('.bak', BackDBFileName)=0
IF NOT EXISTS (SELECT TOP 1 1 FROM #Dir)
BEGIN
   PRINT CHAR(10) + N'在提供的路径下没有找到合符要求的备份文件'    
   Goto ExitFLag
 END
 
 PRINT N'开始还原所有数据库'
DECLARE db_file Cursor Local Static Read_Only Forward_Only
FOR
SELECT BackDBFileName from #Dir
 
OPEN db_file
FETCH NEXT FROM db_file INTO @BackUpFileName
WHILE @@FETCH_STATUS=0
 BEGIN
  --Restore DataBase
  SET @BackUpFullFileName=@BackUpPath+@BackUpFileName
  SET @DbName = REPLACE(@BackUpFileName,'.bak','')
  DELETE FROM #FileListInfo
  INSERT INTO #FileListInfo
    EXEC ('RESTORE FILELISTONLY FROM DISK=''' + @BackUpFullFileName + '''')
	SET @LogicalDbName=''
	SELECT @LogicalDbName = LogicalName FROM   #FileListInfo WHERE [TYPE]= 'D'  --数据库文件  
	SET @LogicalLogName=''
    SELECT @LogicalLogName = LogicalName FROM   #FileListInfo WHERE [TYPE]= 'L' --日志文件
	IF (@LogicalDbName IS NULL OR @LogicalDbName = '')  
   SET @LogicalDbName = @DbName
    IF (@LogicalLogName IS NULL OR @LogicalLogName = '')  
   SET @LogicalLogName = @DbName + '_log'
  SET @RESTORESQL = '  
  IF EXISTS (SELECT * FROM master.dbo.sysdatabases WHERE [name]='''+@DbName+''')
  BEGIN
  EXEC msdb.dbo.sp_delete_database_backuphistory @database_name = N'''+@DbName+'''  
  USE [master]  
  DROP DATABASE ['+@DbName +']  
  END
  RESTORE DATABASE '+@DbName+' FROM DISK = '''+@BackUpFullFileName+'''  
  WITH MOVE '''+@LogicalDbName+''' TO '''+@SqlServerPath+@DbName+'.mdf''
 ,MOVE '''+@LogicalLogName+''' TO '''+@SqlServerPath+@DbName+'_log.ldf'' '
   PRINT @RESTORESQL
   EXECUTE  sp_executesql @RESTORESQL
  FETCH NEXT FROM db_file INTO @BackUpFileName
 END
CLOSE db_file
DEALLOCATE db_file
PRINT N'
所有数据库还原完成'
ExitFLag:
----临时表删除--------
IF (OBJECT_ID('tempdb..#Temp') IS NOT NULL)  
  DROP TABLE #Temp  
IF OBJECT_ID('tempdb..#Dir') IS NOT NULL
    DROP TABLE #Dir
IF OBJECT_ID('tempdb..#FileListInfo') IS NOT NULL
    DROP TABLE #FileListInfo

SqlServer数据库硬盘空间释放

我们在使用sqlserver操作数据的时候,会生成Ldf的日志文件和Mdf数据文件,久而久之,会让硬盘可用空间变小,那么时间长了,我们就需要清除一些日志文件和压缩Mdf数据文件,释放出一些硬盘可用空间

注意:清除数据库Ldf日志文件,将会使数据库之前的操作变得不可逆,Ldf文件有助于我们查找之前执行的脚本,或者生成逆向脚本还原数据库。具体教程将后续说明

压缩数据库Ldf文件

打开数据库文件所在位置,找到需要压缩的Ldf文件,如:我们觉得MY_RelationMgmt_log.ldf文件过大,需要压缩执行以下Sql脚本即可。

USE [master]
GO
ALTER DATABASE MY_RelationMgmt SET RECOVERY SIMPLE WITH NO_WAIT
GO
ALTER DATABASE MY_RelationMgmt SET RECOVERY SIMPLE --先进入简单用户模式
GO
USE [MY_RelationMgmt]
GO
DBCC SHRINKFILE (N'MY_RelationMgmt_Log' , 2, TRUNCATEONLY)
GO

再三说明:此脚本一但执行,将无法查询定位以及回滚之前的操作数据库记录

压缩数据库Mdf文件

打开数据库文件所在位置,找到需要压缩的mdf文件,如:我们觉得MY_RelationMgmt_log.mdf文件过大,需要压缩

因为mdf是数据库数据文件,我们得先查看该数据库当前的每个库表硬盘使用情况

先执行以下sql

use [MY_RelationMgmt]
IF OBJECT_ID('tempdb..#TablesSizes') IS NOT NULL
    DROP TABLE #TablesSizes
CREATE TABLE #TablesSizes
    (
      TableName sysname ,
      Rows BIGINT ,
      reserved VARCHAR(100) ,
      data VARCHAR(100) ,
      index_size VARCHAR(100) ,
      unused VARCHAR(100)
    )
DECLARE @sql VARCHAR(MAX)
SELECT  @sql = COALESCE(@sql, '') + '
INSERT INTO #TablesSizes execute sp_spaceused ''' + QUOTENAME(TABLE_SCHEMA,
                                                              '[]') + '.'
        + QUOTENAME(Table_Name, '[]') + ''''
FROM    INFORMATION_SCHEMA.TABLES
WHERE   TABLE_TYPE = 'BASE TABLE'
PRINT ( @SQL )
EXECUTE (@SQL)
SELECT  *
FROM    #TablesSizes
ORDER BY Rows DESC

看下执行结果,这时候,我们可以看到,每个表的使用情况,比如,我们这2张备份表实际上已经不在使用了,我们可以删除,这时候,我们可以先进行drop table或者Delete Row操作

删除多余的表和数据行以后,在左侧资源管理器选中对应数据库,右键——Tasks——Shrink——DateBase———-

我们看到出现收缩后的库,占当前的比例了,点击OK,完成收缩

SQL SERVER 批量备份所有数据库

有时候,我们希望一次性备份指定的几个SQL SERVER数据库文件,以下有个简单的SQL可以实现

可以自己设定Where的条件,批量备份数据库的sql脚本,可以自行设置备份目录,如果目录不存在会自动创建该目录

(如果脚本复制到查询分析器有红线,不用怕,依旧可以执行)

USE [master]
DECLARE @backupfile NVARCHAR(1024)  
DECLARE @backdesc NVARCHAR(1024)  
DECLARE @filename NVARCHAR(1024)  
DECLARE @path NVARCHAR(1024)  
DECLARE @dbname SYSNAME
DECLARE @extension_name NVARCHAR(50)
SET @path = N'D:\DBBackup\';   -- 此处是服务器的备份文件输出路径
SET @extension_name = N'.bak';
IF (OBJECT_ID('tempdb..#Temp') IS NOT NULL)  
  DROP TABLE #Temp  
CREATE TABLE #Temp (id INT IDENTITY,a INT,b INT,c INT)
DECLARE @fpath NVARCHAR(3)
SET @fpath=LEFT(@path,3)
INSERT #Temp EXEC master..xp_fileexist @fpath
INSERT #Temp EXEC master..xp_fileexist @path
--如果指定盘符有误不存在,则返回错误提示:
IF EXISTS(SELECT 1 FROM #Temp WHERE id=1 AND c=0)
BEGIN
    PRINT CHAR(10) + N'输入的盘符不存在,请重新输入!'
	----临时表删除--------
IF (OBJECT_ID('tempdb..#Temp') IS NOT NULL)  
BEGIN  
  DROP TABLE #Temp  
END
RETURN
END
--如果不存在指定的文件夹,则创建:
ELSE IF EXISTS(SELECT 1 FROM #Temp WHERE b=0 AND id=2)
BEGIN
PRINT CHAR(10) + N'正在创建目录'
EXEC sp_configure 'show advanced options',1
reconfigure
EXEC sp_configure 'xp_cmdshell',1
reconfigure
    DECLARE @mddir NVARCHAR(100)
    SET @mddir='md '+@path
    EXEC master..xp_cmdshell @mddir
	PRINT CHAR(10) + N'创建目录完成'
END
----临时表删除--------
IF (OBJECT_ID('tempdb..#Temp') IS NOT NULL)  
BEGIN  
  DROP TABLE #Temp  
END
--备份
PRINT CHAR(10) + N'开始备份所有数据库'
DECLARE tmp_Cur CURSOR  
FOR  
    SELECT  name  
    FROM    sys.databases  
    WHERE ([name] LIKE '%MGMT' )  -- 此处条件可以自定义,
    ORDER BY Name
 
OPEN tmp_Cur
FETCH NEXT FROM tmp_Cur INTO @dbname;  
WHILE @@FETCH_STATUS = 0    
    BEGIN  
        -- 得到完整目标文件,数据库将备份到这个文件中  
        SET @backupfile = @path + @dbname + @extension_name
        --SELECT  @backupfile  
        SET @backdesc =@dbname + N'-完整 数据库 备份'  
 
        -- 开始备份, COMPRESSION 参数表示压缩,可节省磁盘空间
  PRINT CHAR(10) + N'开始备份 '+@dbname
  PRINT CHAR(10)
        BACKUP DATABASE @dbname TO DISK = @backupfile WITH NOFORMAT, NOINIT,  NAME = @backdesc, SKIP, NOREWIND, NOUNLOAD,  STATS = 10, COMPRESSION  
        PRINT CHAR(10) + @dbname + N' 备份完成 '
        FETCH NEXT FROM tmp_Cur INTO @dbname  
    END  
CLOSE tmp_Cur
DEALLOCATE tmp_Cur
PRINT CHAR(10) + N'所有数据库备份完成'

执行完毕后,就可以在服务器的D:\DBBackup路径上找到这些BAK文件

Centos下的Docker环境RabbitMQ以及SqlServer和MySql搭建

以下为快速构建Centos下的生产环境、包括数据库、消息队列

step1:安装Docker

sudo yum install
 #安装所需的软件包
 sudo yum install -y yum-utils device-mapper-persistent-data lvm2
 #添加Docker稳定版本的yum软件源
 sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
 #更新yum软件源缓存,并安装Docker
 sudo yum install
 sudo yum install -y docker-ce
 #确认Docker服务启动正常
 sudo systemctl start docker

step2:拉取RabbitMQ镜像

docker pull rabbitmq:3.8.3-management

step3:启动RabbitMQ

docker run -d --hostname my-rabbit -p 5672:5672 -p 15672:15672  --name some-rabbit rabbitmq:3.8.3-management

启动命令中设置了映射端口,可以直接访问服务器IP的15672端口来查看,用户名和密码默认都是guest,进入页面后会提示修改密码
到此Docker安装rabbitMq已经完成,且已成功启动。

setp4:拉取SQLServer2019镜像

sudo docker pull mcr.microsoft.com/mssql/server:2019-latest

step5:启动SQLServer2019

docker run -e "ACCEPT_EULA=Y" -e "MSSQL_PID=HMWJ3-KY3J2-NMVD7-KG4JR-X2G8G" -e "MSSQL_SA_PASSWORD=Myun@123jx" --name sqlserver -p 1433:1433 -v /var/opt/mssql:/var/opt/mssql  -d mcr.microsoft.com/mssql/server:2019-latest

step6:连接登录SQLServer后,可以选择执行以下SQL打开代理服务

EXEC sp_configure 'show advanced', 1;
 RECONFIGURE;
 EXEC sp_configure 'allow updates', 0;
 RECONFIGURE;
 EXEC sp_configure 'Agent XPs', 1;
 RECONFIGURE;
 GO

step7:拉取MySQL镜像

docker pull mysql:8.0

step8:在本地宿主机上创建MySQL的配置文件my8.cnf

mkdir ~/mysql8
cd ~/mysql8
mkdir log
vim my8.cnf

以下是my8.cnf内容,以低配的1GB服务器为例子,可以用如下的配置

[mysqld]
pid-file        = /var/run/mysqld/mysqld.pid
socket          = /var/run/mysqld/mysqld.sock
datadir         = /var/lib/mysql
secure-file-priv= NULL
innodb_buffer_pool_size = 16M
performance_schema_max_table_instances=400  
table_definition_cache=400  
table_open_cache=256
performance_schema = off
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0
 
# Custom config should go here
# !includedir /etc/mysql/conf.d/

step9:此后可以把MySQL容器启动,然后把宿主机的配置映射到容器内,为了防止被攻击,可以选择宿主机的非常用端口4306

docker run -d --restart=always -p 4306:3306 -v ~/mysql8/log:/var/log/mysql -v ~/mysql8/my8.cnf:/etc/my.cnf -e MYSQL_ROOT_PASSWORD=数据库密码 -e MYSQL_GENERAL_LOG=1 mysql:8.0

step10:此后MySQL容器配置完成,我们可以通过宿主机的4306端口,访问到容器内的MySQL,最后,我们可以把Docker服务设置为开机启动

systemctl enable docker.service

此外,以下为容器自动重启以及Docker固化命令

 docker update --restart=always 容器id

SQL Server 阻止了对组件“Ad Hoc Distributed Queries”的 STATEMENT“OpenRowset/OpenDatasource”的访问

当SQL Server阻止对组件”Ad Hoc Distributed Queries”的访问时,这是由于服务器的安全配置将此组件关闭所致。”Ad Hoc Distributed Queries”用于允许在SQL Server中执行动态查询并访问其他数据库服务器上的数据。
要启用”Ad Hoc Distributed Queries”,需要使用sp_configure系统存储过程进行配置。以下是一些步骤来启用”Ad Hoc Distributed Queries”:

1.使用sa或具有sysadmin角色的登录名连接到SQL Server。
2.打开SQL Server Management Studio (SSMS)并连接到相应的SQL Server实例。
3.执行以下命令以启用”Ad Hoc Distributed Queries”:

sp_configure 'show advanced options', 1;
 GO
 RECONFIGURE;
 GO
 sp_configure 'Ad Hoc Distributed Queries', 1;
 GO
 RECONFIGURE;
 GO

第一条命令允许显示高级选项,第三条命令启用”Ad Hoc Distributed Queries”选项。RECONFIGURE命令用于应用配置更改。
请注意,启用”Ad Hoc Distributed Queries”可能会存在一些安全风险,因此建议在启用之前评估潜在的风险并采取适当的安全措施。此外,确保只有授权人员可以执行此操作,并且在启用后密切监视使用该功能的查询。
有关更多详细信息和指导,请参考SQL Server的官方文档、SQL Server联机丛书或与数据库管理员进行进一步的讨论