各个开发的同事都写了sql脚本,每个人写的脚本各式各样, 统一放在一个.sql文件里面。
然后一次性执行, 如果有一个地方出错,则全部脚本回滚。
因为我们做的这个是持续集成的自动化发布,全程不能人工干预。
所以需要执行脚本异常则全部回滚。
请问有什么方法吗?
谢谢
5 个解决方案
#1
不知道大家的语句有没有 ddl操作。如果有就不能回滚。
这种升级的操作,一定要严格测试,还要有人值守比较好。
这种升级的操作,一定要严格测试,还要有人值守比较好。
#2
我的目的是这样:
各个开发的同事都写了sql脚本,每个人写的脚本各式各样,统一放在一个.sql文件里面。
然后一次性执行,如果有一个地方出错,则全部脚本回滚。
-----------------------------------------------------------------------------------------------------------------
不现实,我们是做到某地方出错,后续批量脚本(出错位置后面go以后的脚本也会终止),但想回滚,不是不可能,难度太大,例如,修改了某个存储过程,基本上就是灾难性的,回退很难,至于前面版主说的“这种升级的操作,一定要严格测试,还要有人值守比较好。”,我不认同,一个客户还好,有十几个客户你就知道这是不可能的,我们的HIS就是基于互联网自动升级,包括数据库都是自动升级,不敢说没出过问题,但都控制在一个可以掌控的范围内
各个开发的同事都写了sql脚本,每个人写的脚本各式各样,统一放在一个.sql文件里面。
然后一次性执行,如果有一个地方出错,则全部脚本回滚。
-----------------------------------------------------------------------------------------------------------------
不现实,我们是做到某地方出错,后续批量脚本(出错位置后面go以后的脚本也会终止),但想回滚,不是不可能,难度太大,例如,修改了某个存储过程,基本上就是灾难性的,回退很难,至于前面版主说的“这种升级的操作,一定要严格测试,还要有人值守比较好。”,我不认同,一个客户还好,有十几个客户你就知道这是不可能的,我们的HIS就是基于互联网自动升级,包括数据库都是自动升级,不敢说没出过问题,但都控制在一个可以掌控的范围内
#3
把所有脚本内容,都放到一个脚本中,并在一个事务中执行。
#4
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SqlClient;
using System.Data;
namespace ConsoleApplication8
{
class Program
{
static void Main(string[] args)
{
string connString = @"Data Source=(local)\sqlserver2014;Initial Catalog=tempdb;Integrated Security=True";
//正确SQL
string sql_1 =
@"IF OBJECT_ID('t') IS NOT NULL DROP TABLE t
CREATE TABLE t(id INT IDENTITY(1,1) PRIMARY KEY)
GO
ALTER TABLE t ADD c1 INT
go
INSERT INTO t(c1) VALUES(1)
";
//有错误的SQL
string sql_2 =
@"INSERT INTO t(c1) VALUES(2)
GO
if object_id('proc_test') is not null drop proc proc_test
go
CREATE PROC Proc_TEST
AS
Begin
set nocount on
select 1
end
go
123 -- 注:此处有误
";
string errMsg = string.Empty;
bool r1 = ExecuteNonQueryWithConnAndGO(connString, sql_1, ref errMsg);
Console.WriteLine("SQL1 结果:{0}, 错误信息:{1}", r1 ? "成功":"失败", string.IsNullOrEmpty(errMsg)?"无":errMsg);
errMsg = string.Empty;
bool r2 = ExecuteNonQueryWithConnAndGO(connString, sql_2, ref errMsg);
Console.WriteLine("SQL2 结果:{0}, 错误信息:{1}", r2 ? "成功" : "失败", string.IsNullOrEmpty(errMsg) ? "无" : errMsg);
//注:第2个SQL有问题,所以第2个SQL全部回滚了,只有第一个SQL的记录
Print(connString, "select * from t");
Console.Read();
}
#region [ 执行带Go语句 ]
/// <summary>
/// 执行带"GO"的SQL,返回最后一条SQL的受影响行数
/// </summary>
/// <param name="connString">连接串</param>
/// <param name="sql">sql语句</param>
/// <returns>是否成功</returns>
public static bool ExecuteNonQueryWithConnAndGO(string connString, string sql, ref string errMsg)
{
bool result = true;
string[] arr = System.Text.RegularExpressions.Regex.Split(sql, @"\bGO\b", System.Text.RegularExpressions.RegexOptions.IgnoreCase);
using (SqlConnection conn = new SqlConnection(connString))
{
conn.Open();
SqlCommand cmd = new SqlCommand();
cmd.Connection = conn;
SqlTransaction tx = conn.BeginTransaction();
cmd.Transaction = tx;
try
{
for (int n = 0; n < arr.Length; n++)
{
string strsql = arr[n];
if (strsql.Trim().Length > 1 && strsql.Trim().Replace(";", "") != "")
{
cmd.CommandText = strsql;
cmd.ExecuteNonQuery();
}
}
tx.Commit();
}
catch (System.Data.SqlClient.SqlException ex)
{
tx.Rollback();
result = false;
errMsg = ex.Message;
}
}
return result;
}
#endregion
public static void Print(string connString, string sql)
{
Console.WriteLine("\r\n------- 输出信息: {0} --------", sql);
DataTable dt = new DataTable();
using (SqlConnection conn = new SqlConnection(connString))
{
conn.Open();
SqlCommand cmd = new SqlCommand(sql, conn);
SqlDataAdapter adapter = new SqlDataAdapter(cmd);
adapter.Fill(dt);
}
foreach (DataRow dr in dt.Rows)
{
foreach (DataColumn dc in dt.Columns)
{
Console.Write("{0}: {1}\t",dc.ColumnName,dr[dc.ColumnName].ToString());
}
Console.WriteLine();
}
}
}
}
仿照这个做就是了。
#5
我看到的很多客户是不管HIS,还是ERP升级的话,还是都是出错了就抛出错误,解决错误了,就可以整个全部重新执行。在每个脚本里面要做好判断,保证语句都是可以重新执行的。
#1
不知道大家的语句有没有 ddl操作。如果有就不能回滚。
这种升级的操作,一定要严格测试,还要有人值守比较好。
这种升级的操作,一定要严格测试,还要有人值守比较好。
#2
我的目的是这样:
各个开发的同事都写了sql脚本,每个人写的脚本各式各样,统一放在一个.sql文件里面。
然后一次性执行,如果有一个地方出错,则全部脚本回滚。
-----------------------------------------------------------------------------------------------------------------
不现实,我们是做到某地方出错,后续批量脚本(出错位置后面go以后的脚本也会终止),但想回滚,不是不可能,难度太大,例如,修改了某个存储过程,基本上就是灾难性的,回退很难,至于前面版主说的“这种升级的操作,一定要严格测试,还要有人值守比较好。”,我不认同,一个客户还好,有十几个客户你就知道这是不可能的,我们的HIS就是基于互联网自动升级,包括数据库都是自动升级,不敢说没出过问题,但都控制在一个可以掌控的范围内
各个开发的同事都写了sql脚本,每个人写的脚本各式各样,统一放在一个.sql文件里面。
然后一次性执行,如果有一个地方出错,则全部脚本回滚。
-----------------------------------------------------------------------------------------------------------------
不现实,我们是做到某地方出错,后续批量脚本(出错位置后面go以后的脚本也会终止),但想回滚,不是不可能,难度太大,例如,修改了某个存储过程,基本上就是灾难性的,回退很难,至于前面版主说的“这种升级的操作,一定要严格测试,还要有人值守比较好。”,我不认同,一个客户还好,有十几个客户你就知道这是不可能的,我们的HIS就是基于互联网自动升级,包括数据库都是自动升级,不敢说没出过问题,但都控制在一个可以掌控的范围内
#3
把所有脚本内容,都放到一个脚本中,并在一个事务中执行。
#4
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SqlClient;
using System.Data;
namespace ConsoleApplication8
{
class Program
{
static void Main(string[] args)
{
string connString = @"Data Source=(local)\sqlserver2014;Initial Catalog=tempdb;Integrated Security=True";
//正确SQL
string sql_1 =
@"IF OBJECT_ID('t') IS NOT NULL DROP TABLE t
CREATE TABLE t(id INT IDENTITY(1,1) PRIMARY KEY)
GO
ALTER TABLE t ADD c1 INT
go
INSERT INTO t(c1) VALUES(1)
";
//有错误的SQL
string sql_2 =
@"INSERT INTO t(c1) VALUES(2)
GO
if object_id('proc_test') is not null drop proc proc_test
go
CREATE PROC Proc_TEST
AS
Begin
set nocount on
select 1
end
go
123 -- 注:此处有误
";
string errMsg = string.Empty;
bool r1 = ExecuteNonQueryWithConnAndGO(connString, sql_1, ref errMsg);
Console.WriteLine("SQL1 结果:{0}, 错误信息:{1}", r1 ? "成功":"失败", string.IsNullOrEmpty(errMsg)?"无":errMsg);
errMsg = string.Empty;
bool r2 = ExecuteNonQueryWithConnAndGO(connString, sql_2, ref errMsg);
Console.WriteLine("SQL2 结果:{0}, 错误信息:{1}", r2 ? "成功" : "失败", string.IsNullOrEmpty(errMsg) ? "无" : errMsg);
//注:第2个SQL有问题,所以第2个SQL全部回滚了,只有第一个SQL的记录
Print(connString, "select * from t");
Console.Read();
}
#region [ 执行带Go语句 ]
/// <summary>
/// 执行带"GO"的SQL,返回最后一条SQL的受影响行数
/// </summary>
/// <param name="connString">连接串</param>
/// <param name="sql">sql语句</param>
/// <returns>是否成功</returns>
public static bool ExecuteNonQueryWithConnAndGO(string connString, string sql, ref string errMsg)
{
bool result = true;
string[] arr = System.Text.RegularExpressions.Regex.Split(sql, @"\bGO\b", System.Text.RegularExpressions.RegexOptions.IgnoreCase);
using (SqlConnection conn = new SqlConnection(connString))
{
conn.Open();
SqlCommand cmd = new SqlCommand();
cmd.Connection = conn;
SqlTransaction tx = conn.BeginTransaction();
cmd.Transaction = tx;
try
{
for (int n = 0; n < arr.Length; n++)
{
string strsql = arr[n];
if (strsql.Trim().Length > 1 && strsql.Trim().Replace(";", "") != "")
{
cmd.CommandText = strsql;
cmd.ExecuteNonQuery();
}
}
tx.Commit();
}
catch (System.Data.SqlClient.SqlException ex)
{
tx.Rollback();
result = false;
errMsg = ex.Message;
}
}
return result;
}
#endregion
public static void Print(string connString, string sql)
{
Console.WriteLine("\r\n------- 输出信息: {0} --------", sql);
DataTable dt = new DataTable();
using (SqlConnection conn = new SqlConnection(connString))
{
conn.Open();
SqlCommand cmd = new SqlCommand(sql, conn);
SqlDataAdapter adapter = new SqlDataAdapter(cmd);
adapter.Fill(dt);
}
foreach (DataRow dr in dt.Rows)
{
foreach (DataColumn dc in dt.Columns)
{
Console.Write("{0}: {1}\t",dc.ColumnName,dr[dc.ColumnName].ToString());
}
Console.WriteLine();
}
}
}
}
仿照这个做就是了。
#5
我看到的很多客户是不管HIS,还是ERP升级的话,还是都是出错了就抛出错误,解决错误了,就可以整个全部重新执行。在每个脚本里面要做好判断,保证语句都是可以重新执行的。