Я на самом деле решил это сам в конце путем управления реестром. Я создал класс для содержания функциональности, содержание которой я включал здесь:
///<summary>
/// Class to assist with creation and removal of ODBC DSN entries
///</summary>
public static class ODBCManager
{
private const string ODBC_INI_REG_PATH = "SOFTWARE\\ODBC\\ODBC.INI\\";
private const string ODBCINST_INI_REG_PATH = "SOFTWARE\\ODBC\\ODBCINST.INI\\";
/// <summary>
/// Creates a new DSN entry with the specified values. If the DSN exists, the values are updated.
/// </summary>
/// <param name="dsnName">Name of the DSN for use by client applications</param>
/// <param name="description">Description of the DSN that appears in the ODBC control panel applet</param>
/// <param name="server">Network name or IP address of database server</param>
/// <param name="driverName">Name of the driver to use</param>
/// <param name="trustedConnection">True to use NT authentication, false to require applications to supply username/password in the connection string</param>
/// <param name="database">Name of the datbase to connect to</param>
public static void CreateDSN(string dsnName, string description, string server, string driverName, bool trustedConnection, string database)
{
// Lookup driver path from driver name
var driverKey = Registry.LocalMachine.CreateSubKey(ODBCINST_INI_REG_PATH + driverName);
if (driverKey == null) throw new Exception(string.Format("ODBC Registry key for driver '{0}' does not exist", driverName));
string driverPath = driverKey.GetValue("Driver").ToString();
// Add value to odbc data sources
var datasourcesKey = Registry.LocalMachine.CreateSubKey(ODBC_INI_REG_PATH + "ODBC Data Sources");
if (datasourcesKey == null) throw new Exception("ODBC Registry key for datasources does not exist");
datasourcesKey.SetValue(dsnName, driverName);
// Create new key in odbc.ini with dsn name and add values
var dsnKey = Registry.LocalMachine.CreateSubKey(ODBC_INI_REG_PATH + dsnName);
if (dsnKey == null) throw new Exception("ODBC Registry key for DSN was not created");
dsnKey.SetValue("Database", database);
dsnKey.SetValue("Description", description);
dsnKey.SetValue("Driver", driverPath);
dsnKey.SetValue("LastUser", Environment.UserName);
dsnKey.SetValue("Server", server);
dsnKey.SetValue("Database", database);
dsnKey.SetValue("Trusted_Connection", trustedConnection ? "Yes" : "No");
}
/// <summary>
/// Removes a DSN entry
/// </summary>
/// <param name="dsnName">Name of the DSN to remove.</param>
public static void RemoveDSN(string dsnName)
{
// Remove DSN key
Registry.LocalMachine.DeleteSubKeyTree(ODBC_INI_REG_PATH + dsnName);
// Remove DSN name from values list in ODBC Data Sources key
var datasourcesKey = Registry.LocalMachine.CreateSubKey(ODBC_INI_REG_PATH + "ODBC Data Sources");
if (datasourcesKey == null) throw new Exception("ODBC Registry key for datasources does not exist");
datasourcesKey.DeleteValue(dsnName);
}
///<summary>
/// Checks the registry to see if a DSN exists with the specified name
///</summary>
///<param name="dsnName"></param>
///<returns></returns>
public static bool DSNExists(string dsnName)
{
var driversKey = Registry.LocalMachine.CreateSubKey(ODBCINST_INI_REG_PATH + "ODBC Drivers");
if (driversKey == null) throw new Exception("ODBC Registry key for drivers does not exist");
return driversKey.GetValue(dsnName) != null;
}
///<summary>
/// Returns an array of driver names installed on the system
///</summary>
///<returns></returns>
public static string[] GetInstalledDrivers()
{
var driversKey = Registry.LocalMachine.CreateSubKey(ODBCINST_INI_REG_PATH + "ODBC Drivers");
if (driversKey == null) throw new Exception("ODBC Registry key for drivers does not exist");
var driverNames = driversKey.GetValueNames();
var ret = new List<string>();
foreach (var driverName in driverNames)
{
if (driverName != "(Default)")
{
ret.Add(driverName);
}
}
return ret.ToArray();
}
}
Для этого вы можете использовать XQuery - просто измените свой оператор на:
SELECT
CAST(parms as xml).value('(/xml/@filename)[1]', 'varchar(260)') as p
FROM
dbo.Table1
Это дает вам VARCHAR (260), достаточно длинный для хранения любого допустимого имени файла и пути - теперь у вас есть строка и может работать с ним с SUBSTRING и т. д.
Marc
К сожалению, SQL Server не является соответствующей реализацией XQuery - скорее, это довольно ограниченное подмножество черновой версии спецификации XQuery. В нем не только нет fn: substring-before
, но также нет fn: index-of
, чтобы сделать это самостоятельно, используя fn: substring
, ни fn: строка-код
. Итак, насколько я могу судить, здесь вы застряли с SQL.
Самый простой способ сделать это - использовать SUBSTRING и CHARINDEX. Предполагая (мудро или нет), что первая часть имени файла не меняет длину, но вы по-прежнему хотите использовать XQuery для поиска имени файла, вот короткое повторение, которое делает то, что вы хотите:
declare @t table (
parms varchar(max)
);
insert into @t values ('<xml filename="100100_456_484351864768.zip" event_dt="10/5/2009 11:42:52 AM"><info user="TestUser" /></xml>');
with T(fName) as (
select cast(cast(parms as xml).query('data(/xml/@filename)') as varchar(100)) as p
from @t
)
select
substring(fName,8,charindex('_',fName,8)-8) as myNum
from T;
Есть хитрые решения, которые используйте другие строковые функции, такие как REPLACE и PARSENAME или REVERSE, но ни одна из них, вероятно, не будет более эффективной или удобочитаемой. Одна из возможностей - написать подпрограмму CLR, которая привносит обработку регулярных выражений в SQL.
Кстати, если ваш xml всегда такой простой, у меня нет особой причины использовать XQuery. Вот два запроса, которые извлекут нужное вам число. Второй вариант безопаснее, если у вас нет контроля над лишними пробелами в строке xml или над возможностью изменения длины первой части имени файла:
select
substring(parms,23,charindex('_',parms,23)-23) as myNum
from @t;
select
substring(parms,charindex('_',parms)+1,charindex('_',parms,charindex('_',parms)+1)-charindex('_',parms)-1) as myNum
from @t;