У @Shashank правильная точка зрения. То, что вы хотите сделать, это отключить ввод специальных символов из бэкэнда, а также сделать поле недействительным из веб-интерфейса. Хакер, зная, что он делает, легко манипулирует самим HTTP-запросом, а не полем, что делает его довольно уязвимым.
Однако, если вы настаиваете на своем решении, я бы рекомендовал использовать RegEx. Это примерное выражение может пригодиться. Таким образом, всякий раз, когда поле ввода обнаруживает один из этих специальных символов, оно заменяет этот символ на ''
Считайте это... Проклятие и Благословения Динамического SQL, помогите мне большое понимание, как решить этот тип проблем.
Нет никакого "более опрятного" способа сделать это. Вы сэкономите время, если Вы примете его и посмотрите на что-то еще.
Править: Ага! Относительно комментария OP, что "Мы должны загрузить данные в новую базу данных каждый месяц или иначе это становится слишком большим".. удивляющий ретроспективно, который никто не отметил относительно слабого запаха этой проблемы.
SQL Server предлагает собственные механизмы для контакта с таблицами, которые становятся "слишком большими" (в частности, деля), который позволит Вам обращаться к таблице как к единственному объекту, при делении таблицы на отдельные файлы в фоновом режиме, таким образом устранении текущей проблемы в целом.
Другими словами, это - проблема для Вашего администратора DB, не потребителя DB. Если это, оказывается, Вы также, я предлагаю, чтобы Вы изучили разделение этой таблицы.
попробуйте sp_executesql, созданный в функции. Можно в основном создать строку SQL в proc, затем звонить
exec sp_executesql @SQLString.
DECLARE @SQLString nvarchar(max)
SELECT @SQLString = '
SELECT *
FROM ' + @TableName
EXEC sp_executesql @SQLString
Вы не можете указать динамическое имя таблицы в SQL Server.
Существует несколько опций:
Вы сказали, что Вам не нравится 1, поэтому отпускает для 2.
Первая опция состоит в том, чтобы ограничить грязность одной строкой:
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;
Я не уверен, что мне нравится это, но это может быть Ваш наилучший вариант. Таким образом, все ВЫБОРЫ будут тем же.
Можно осуществить рефакторинг это к содержанию основ, но существует много недостатков к этому, включая синоним создается в транзакции, таким образом, у Вас не может быть двух из запросов, работающих одновременно (потому что оба будут пытаться создать temptablesyn). В зависимости от стратегии блокировки каждый заблокирует другой.
Синонимы являются постоянными, таким образом, это - то, почему необходимо сделать это в транзакции.
Существует несколько опций, но они более грязны, чем способ, которым Вы уже делаете. Я предлагаю Вас также:
(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. Если таблицы являются большими, Вы могли бы рассмотреть использование индексного представления, индексированного на новом database_name столбце (или независимо от того, что Вы называете его), и первичный ключ таблиц (я предполагаю от вопроса, что схемы таблиц являются тем же?).
Очевидно, если Ваш список базы данных часто изменяется, то это становится более проблематичным - но если необходимо создать эти базы данных так или иначе, затем поддержание этого представления одновременно не должно быть слишком большим количеством издержек!
Я думаю, что Mark Brittingham имеет верное представление (здесь: http://stackoverflow.com/questions/688425/evaluate-in-t-sql/718223#718223), который является к проблеме a use database
управляйте и запишите SP для НЕ полного определения имени таблицы. Как он отмечает, это будет действовать на таблицы в текущей базе данных входа в систему.
Позвольте мне добавить несколько возможных разработок:
Из комментария OP я заключаю, что база данных изменяется один раз в месяц, когда это становится "слишком большим". ("Мы должны загрузить данные в новую базу данных каждый месяц, или иначе это становится слишком большим. – d03boy")
Пользовательские логины имеют базу данных по умолчанию, устанавливают с (удержанным от использования) sp_defaultdb или ИЗМЕНЯЮТ ВХОД В СИСТЕМУ. Если каждый месяц Вы идете дальше к новой базе данных, и не должны выполнить SP на более старых копиях, просто изменить дб входа в систему по умолчанию ежемесячно, и снова, не полностью определяйте имя таблицы.
База данных для использования может быть установлена в клиентском входе в систему: sqlcmd -U login_id -P password -d db_name
, затем должностное лицо SP оттуда.
Можно установить соединение с базой данных с помощью клиента по Вашему выбору (командная строка, ODBC, JDBC), затем проблема a use database
команда, должностное лицо SP.
используйте панель базы данных; должностное лицо sp_foo;
После того как база данных была установлена с помощью одного из вышеупомянутого, у Вас есть три варианта для выполнения хранимой процедуры:
Вы могли просто скопировать SP наряду с базой данных, в к новой базе данных. Пока имя таблицы НЕ полностью определяется, Вы будете воздействовать на таблицу новой базы данных.
должностное лицо sp_foo;
Вы могли установить единственную каноническую копию SP в его собственной базе данных, назвать его procs
, с именем таблицы, не полностью определенным, и затем, называют его полностью полностью определенное имя:
должностное лицо procs.dbo.sp_foo;
Вы, в каждой отдельной базе данных, могли установить тупик sp_foo
то, что должностные лица полностью определенное название реального SP и затем должностное лицо sp_foo
не квалифицируя его. Тупик назовут, и он призовет реальную процедуру procs
. (К сожалению, use database dbname
не может быть выполнен из SP.)
--sp_foo stub: create proc bar.dbo.sp_foo @parm int as begin exec procs.dbo.sp_foo @parm; end go
Однако это сделано, если база данных изменяется, реальный SP должен быть создан с WITH RECOMPILE
опция, иначе это будет кэшировать план выполнения относительно неправильной таблицы. Тупику, конечно, не нужно это.
Вы могли создать CLR SQL Табличный UDF для доступа к таблицам. Необходимо связать его со схемой, потому что ТВ-UDFs не поддерживает динамическую схему. (Мой образец включает идентификатор, и столбец Title - изменяют для Ваших потребностей),
После того как Вы сделали это, необходимо смочь сделать следовать запрос:
SELECT * FROM dbo.FromMyTable('table1')
Можно включать многослойное имя в ту строку также.
SELECT * FROM dbo.FromMyTable('otherdb..table1')
возвратить идентификатор, столбцы Title от той таблицы.
Необходимо будет, вероятно, включить SQL CLR и включить опцию TRUSTWORTHY:
sp_configure 'clr enabled',1
go
reconfigure
go
alter database mydatabase set trustworthy on
Создайте Проект SQL C#, добавьте новый файл UDF, вставьте это там. Установите Свойство проекта, Базу данных, Уровень Разрешения к внешнему. Создайте, развернитесь. Может обойтись без VisualStudio. Сообщите мне, нужно ли Вам это.
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();
}
}
}
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
...
можно генерировать этот proc как часть сценариев создания базы данных при изменении списка баз данных, доступных запросу.
Это избегает проблем безопасности с динамическим sql. Можно также улучшить производительность путем замены 'избранных' операторов хранимыми процедурами, предназначающимися для каждой базы данных (1 кэшируемый план выполнения на запрос).
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