Sql server 2005 用T-SQL进行计算
我有一个存储过程,它允许一个IN参数指定要使用的数据库。然后,我使用该数据库中预先确定的表进行查询。我遇到的问题是将表名连接到查询中的数据库名。如果T-SQL有一个求值函数,我可以执行以下操作Sql server 2005 用T-SQL进行计算,sql-server-2005,tsql,evaluation,Sql Server 2005,Tsql,Evaluation,我有一个存储过程,它允许一个IN参数指定要使用的数据库。然后,我使用该数据库中预先确定的表进行查询。我遇到的问题是将表名连接到查询中的数据库名。如果T-SQL有一个求值函数,我可以执行以下操作 eval(@dbname + 'MyTable') 目前,我一直在创建一个字符串,然后使用exec()将该字符串作为查询运行。这很混乱,我不想创建字符串。有没有一种方法可以对变量或字符串求值,以便执行以下操作 SELECT * FROM eval(@dbname + 'MyTable') 我希望对其进
eval(@dbname + 'MyTable')
目前,我一直在创建一个字符串,然后使用exec()
将该字符串作为查询运行。这很混乱,我不想创建字符串。有没有一种方法可以对变量或字符串求值,以便执行以下操作
SELECT *
FROM eval(@dbname + 'MyTable')
我希望对其进行评估,使其最终显示如下:
SELECT *
FROM myserver.mydatabase.dbo.MyTable
尝试sp_executesql内置函数。 您基本上可以在proc中构建SQL字符串,然后调用
exec sp_executesql @SQLString.
DECLARE @SQLString nvarchar(max)
SELECT @SQLString = '
SELECT *
FROM ' + @TableName
EXEC sp_executesql @SQLString
没有“更整洁”的方法可以做到这一点。如果你接受它,看看别的东西,你就会节省时间
编辑:啊哈!关于OP的评论,“我们必须每个月将数据加载到一个新的数据库中,否则它会变得太大。”。令人惊讶的是,回想起来,没有人评论这个问题的微弱气味
SQL Server提供了本机机制来处理“太大”的表(特别是分区),这将允许您将表作为单个实体进行寻址,同时在后台将表划分为单独的文件,从而完全消除当前的问题
换句话说,这是数据库管理员的问题,而不是数据库使用者的问题。如果您碰巧也是这样,我建议您查看此表。您不能在SQL Server中指定动态表名 有几种选择:
begin transaction t1;
declare @statement nvarchar(100);
set @statement = 'create synonym temptablesyn for db1.dbo.test;'
exec sp_executesql @statement
select * from db_syn
drop synonym db_syn;
rollback transaction t1;
我不确定我是否喜欢这个,但它可能是你最好的选择。这样,所有选择都将是相同的
您可以将其重构为您的核心内容,但这有许多缺点,包括同义词是在事务中创建的,因此不能有两个
在同一时间运行的查询的
时间(因为双方都在努力
创建一个新的数据库(Syn)。依靠
根据锁定策略,您将
挡住另一个
同义词是永久的,所以这就是为什么您需要在事务中执行此操作。阅读此,帮助我了解如何解决这类问题 有一些选择,但它们比你现在的方式更混乱。我建议你:
(1) 坚持当前的方法
(2) 尽管如此,还是要在代码中嵌入SQL。
(3) 要格外小心地验证您的输入,以避免SQL注入 此外,混乱不是动态SQL的唯一问题。记住以下几点:
(1) 动态SQL阻碍了服务器创建可重用执行计划的能力。
(2) ExecuteSQL命令会断开所有权链。这意味着代码将在调用存储过程的用户而不是过程所有者的上下文中运行。这可能会迫使您在语句所针对的任何表上打开安全性,并创建其他安全问题。这只是一个想法,但如果您有这些数据库的预定义列表,则可以在数据库中创建一个视图,供您连接以加入它们-类似于:
CREATE VIEW dbo.all_tables
AS
SELECT your_columns,
'db_name1' AS database_name
FROM db_name1.dbo.your_table
UNION ALL
SELECT your_columns,
'db_name2'
FROM db_name2.dbo.your_table
etc...
然后,您可以将数据库名称传递到存储过程中,并在WHERE子句中将其用作参数。如果表很大,您可以考虑使用索引视图,在新的DabaseEnEngy列(或您所称的)和表的主键上索引(假设从表的模式相同的问题来看)。
显然,如果您的数据库列表经常更改,那么问题就更大了——但是如果您必须创建这些数据库,那么同时维护这个视图应该不会有太大的开销
declare @sql varchar(256);
set @sql = 'select * into ##myGlobalTemporaryTable from '+@dbname
exec sp_executesql @sql
select * from ##myGlobalTemporaryTable
复制到一个全局临时表中,然后可以像常规表一样使用该表。如果数据库数量可以合理管理,最好使用预定义的条件语句,如:
if (@dbname = 'db1')
select * from db1..MyTable
if (@dbname = 'db2')
select * from db2..MyTable
if (@dbname = 'db3')
select * from db3..MyTable
如果要更改可查询的数据库列表,可以将此过程作为数据库创建脚本的一部分生成
这避免了动态sql的安全问题。您还可以通过将“select”语句替换为针对每个数据库的存储过程(每个查询一个缓存执行计划)来提高性能。我认为Mark Brittingham的想法是正确的(这里: h) ,即发出
use database
命令并写入sp以不完全限定表名。正如他所指出的,这将作用于登录当前数据库中的表
让我补充一些可能的细节:
根据OP的一条评论,我收集到当数据库变得“太大”时,每个月都会更改一次数据库。(“我们必须每月将数据加载到一个新数据库中,否则它会变得太大。”–d03boy)
sqlcmd-U login\U id-P password-d db\U name
,然后从那里执行spuse database
命令,执行sp
使用数据库栏;
执行秘书斯普富SELECT * FROM dbo.FromMyTable('table1')
SELECT * FROM dbo.FromMyTable('otherdb..table1')
sp_configure 'clr enabled',1
go
reconfigure
go
alter database mydatabase set trustworthy on
using System;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using System.Collections;
using System.Data.SqlClient;
[assembly: CLSCompliant(true)]
namespace FromMyTable
{
public static partial class UserDefinedFunctions
{
[Microsoft.SqlServer.Server.SqlFunction(DataAccess = DataAccessKind.Read, IsDeterministic = true, SystemDataAccess = SystemDataAccessKind.Read, IsPrecise = true, FillRowMethodName = "FillRow",
TableDefinition = "id int, title nvarchar(1024)")]
public static IEnumerable FromMyTable(SqlString tableName)
{
return new FromMyTable(tableName.Value);
}
public static void FillRow(object row, out SqlInt32 id, out SqlString title)
{
MyTableSchema v = (MyTableSchema)row;
id = new SqlInt32(v.id);
title = new SqlString(v.title);
}
}
public class MyTableSchema
{
public int id;
public string title;
public MyTableSchema(int id, string title) { this.id = id; this.title = title; }
}
internal class FromMyTable : IEnumerable
{
string tableName;
public FromMyTable(string tableName)
{
this.tableName = tableName;
}
public IEnumerator GetEnumerator()
{
return new FromMyTableEnum(tableName);
}
}
internal class FromMyTableEnum : IEnumerator
{
SqlConnection cn;
SqlCommand cmd;
SqlDataReader rdr;
string tableName;
public FromMyTableEnum(string tableName)
{
this.tableName = tableName;
Reset();
}
public MyTableSchema Current
{
get { return new MyTableSchema((int)rdr["id"], (string)rdr["title"]); }
}
object IEnumerator.Current
{
get { return Current; }
}
public bool MoveNext()
{
bool b = rdr.Read();
if (!b) { rdr.Dispose(); cmd.Dispose(); cn.Dispose(); rdr = null; cmd = null; cn = null; }
return b;
}
public void Reset()
{
// note: cannot use a context connection here because it will be closed
// in between calls to the enumerator.
if (cn == null) { cn = new SqlConnection("server=localhost;database=mydatabase;Integrated Security=true;"); cn.Open(); }
if (cmd == null) cmd = new SqlCommand("select id, title FROM " + tableName, cn);
if (rdr != null) rdr.Dispose();
rdr = cmd.ExecuteReader();
}
}
}
if exists (select * from master..sysservers where srvname = 'fromdb')
exec sp_dropserver 'fromdb'
go
declare @mydb nvarchar(99);
set @mydb='mydatabase'; -- variable to select database
exec sp_addlinkedserver @server = N'fromdb',
@srvproduct = N'',
@provider = N'SQLOLEDB',
@datasrc = @@servername,
@catalog = @mydb
go
select * from OPENQUERY(fromdb, 'select * from table1')
go