Issue
We are in the process of changing a database name on our server, but the database is huge and we want to create a small application that will loop through all databases, and then all the tables in the databases and then all the columns in the tables for text that matches "LIVE".
Code
I started creating code but i feel like this way in clunky and is returning an error.
using (SqlConnection connection = new SqlConnection(#"data source="SERVER NAME";integrated security=false;uid="USER";password="PASSWORD";"))
{
connection.Open();
SqlCommand command = new SqlCommand(#"SELECT * FROM sys.databases", connection);
SqlDataReader reader = command.ExecuteReader();
try
{
while (reader.Read())
{
_databases.Add(string.Format("{0}", reader["name"]));
}
}
finally
{
reader.Close();
}
foreach (var database in _databases)
{
using (SqlConnection tableconnection = new SqlConnection(string.Format(#"data source="SERVER NAME";initial catalog=" + database + ";integrated security=false;uid="USERNAME";password="PASSWORD";")))
{
tableconnection.Open();
SqlCommand tablecommand = new SqlCommand(#"SELECT * FROM information_schema.tables", tableconnection);
SqlDataReader readertable = tablecommand.ExecuteReader();
try
{
while (readertable.Read())
{
_tables.Add(string.Format("{0}", readertable["TABLE_NAME"]));
}
}
finally
{
readertable.Close();
}
foreach (var tables in _tables)
{
SqlCommand columncommand = new SqlCommand(string.Format(#"SELECT * FROM INFORMATION_SCHEMA.COLUMNS where TABLE_Name=" + tables, tableconnection));
SqlDataReader readercolumn = columncommand.ExecuteReader();
try
{
while (readercolumn.Read())
{
_columns.Add(string.Format("{0}", readercolumn["COLUMN_NAME"]));
}
}
finally
{
readercolumn.Close();
}
}
}
}
}
As you can see I am unsure if this is the best way to do it. The code errors on:
SqlDataReader readercolumn = columncommand.ExecuteReader();
and the error is:
Additional information: ExecuteReader: Connection property has not been initialized.
Does anyone know what is going wrong or a simple way to do this?
You can use this for your problem :). :
CREATE PROCEDURE dbo.SearchAllDatabases
#SearchTerm NVARCHAR(255) = NULL
AS
BEGIN
SET NOCOUNT ON;
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
IF #SearchTerm IS NULL OR #SearchTerm NOT LIKE N'%[^%^_]%'
BEGIN
RAISERROR(N'Please enter a valid search term.', 11, 1);
RETURN;
END
CREATE TABLE #results
(
[database] SYSNAME,
[schema] SYSNAME,
[table] SYSNAME,
[column] SYSNAME,
ExampleValue NVARCHAR(1000)
);
DECLARE
#DatabaseCommands NVARCHAR(MAX) = N'',
#ColumnCommands NVARCHAR(MAX) = N'';
SELECT #DatabaseCommands = #DatabaseCommands + N'
EXEC ' + QUOTENAME(name) + '.sys.sp_executesql
#ColumnCommands, N''#SearchTerm NVARCHAR(MAX)'', #SearchTerm;'
FROM sys.databases
WHERE database_id > 4 -- non-system databases
AND[state] = 0-- online
AND user_access = 0; -- multi-user
SET #ColumnCommands = N'DECLARE #q NCHAR(1),
#SearchCommands NVARCHAR(MAX);
SELECT #q = NCHAR(39),
#SearchCommands = N''DECLARE #VSearchTerm VARCHAR(255) = #SearchTerm;'';
SELECT #SearchCommands = #SearchCommands + CHAR(10) + N''
SELECT TOP(1)
[db] = DB_NAME(),
[schema] = N'' + #q + s.name + #q + '',
[table] = N'' + #q + t.name + #q + '',
[column] = N'' + #q + c.name + #q + '',
ExampleValue = LEFT('' + QUOTENAME(c.name) + '', 1000)
FROM '' + QUOTENAME(s.name) + ''.'' + QUOTENAME(t.name) + ''
WHERE '' + QUOTENAME(c.name) + N'' LIKE #'' + CASE
WHEN c.system_type_id IN(35, 167, 175) THEN ''V''
ELSE '''' END + ''SearchTerm;''
FROM sys.schemas AS s
INNER JOIN sys.tables AS t
ON s.[schema_id] = t.[schema_id]
INNER JOIN sys.columns AS c
ON t.[object_id] = c.[object_id]
WHERE c.system_type_id IN (35, 99, 167, 175, 231, 239)
AND c.max_length >= LEN(#SearchTerm);
PRINT #SearchCommands;
EXEC sys.sp_executesql #SearchCommands,
N''#SearchTerm NVARCHAR(255)'', #SearchTerm;';
INSERT #Results
(
[database],
[schema],
[table],
[column],
ExampleValue
)
EXEC[master].sys.sp_executesql #DatabaseCommands,
N'#ColumnCommands NVARCHAR(MAX), #SearchTerm NVARCHAR(255)',
#ColumnCommands, #SearchTerm;
SELECT[Searched for] = #SearchTerm;
SELECT[database],[schema],[table],[column],ExampleValue
FROM #Results
ORDER BY[database],[schema],[table],[column];
END
GO
And use it like this :
exec SearchAllDatabases #SearchTerm = '%value%'
Or execute this stored procedure from your code.
using (SqlConnection connection = new SqlConnection(#"data source="SERVER NAME";integrated security=false;uid="USER";password="PASSWORD";"))
{
conn.Open();
// 1. create a command object identifying the stored procedure
SqlCommand cmd = new SqlCommand("SearchAllDatabases", conn);
// 2. set the command object so it knows to execute a stored procedure
cmd.CommandType = CommandType.StoredProcedure;
// 3. add parameter to command, which will be passed to the stored procedure
cmd.Parameters.Add(new SqlParameter("#SearchTerm", value));
cmd.ExecuteNonQuery();
}
Your code
SqlCommand columncommand = new SqlCommand(string.Format(#"SELECT * FROM INFORMATION_SCHEMA.COLUMNS where TABLE_Name=" + tables, tableconnection));
The end of the statement has )), putting table connection as a parameter for string.Format, which you dont use, so either, move it or...
You should have:
SqlCommand columncommand = new SqlCommand(#"SELECT * FROM INFORMATION_SCHEMA.COLUMNS where TABLE_Name=" + tables, tableconnection);
Related
I have a stored procedure where the arguments looks like this
Create Procedure [dbo].[myStoredProcedure]
#TaskId int = 0
, #FileName varchar(200) =''
, #DataDtFrom smalldatetime = '01/01/1900'
, #DataDtTo smalldatetime = '01/01/1900'
, #OFFSET INT = 0
, #FETCH INT = 2000
, #WhereClauseString varchar(5000) = ''
SELECT
DataDt
,EffDt
,LoanNumber
,UploadDate
,UploadedFileName
FROM dbo.myFileTable u
WHERE
(#DataDtTo = '01/01/1900' or DataDt between #DataDtFrom and #DataDtTo)
and (#TaskId = 0 or TaskId = #TaskId)
and (#FileName = '' or UploadedFileName like '%' + #FileName + '%')
**Where ??? = #WhereClauseString**
ORDER BY u.UploadDate
OFFSET #OFFSET ROWS
FETCH NEXT #FETCH ROWS ONLY
I initialize this in C#
var whereClauseString = "LoanNum in(111,222,444) and TaskId in (123,456,789)";
using (var conn = new MyEntities().Database.Connection)
{
conn.Open();
var cmd = conn.CreateCommand();
cmd.CommandTimeout = 1800;
cmd.CommandText = model.UploadStoredProcedure;
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.Parameters.Add(new System.Data.SqlClient.SqlParameter("#TaskId", Convert.ToInt64(model.TaskId)));
cmd.Parameters.Add(new System.Data.SqlClient.SqlParameter("#FileName", model.FileName ?? string.Empty));
cmd.Parameters.Add(new System.Data.SqlClient.SqlParameter("#DataDtFrom", DateTime.Parse(model.adjFromDataDt.ToShortDateString()) <= DateTime.Parse(basicDate.ToShortDateString()) ? basicDate : model.adjFromDataDt.Date));
cmd.Parameters.Add(new System.Data.SqlClient.SqlParameter("#DataDtTo", DateTime.Parse(model.adjToDataDt.ToShortDateString()) <= DateTime.Parse(basicDate.ToShortDateString()) ? basicDate : model.adjToDataDt.Date));
cmd.Parameters.Add(new System.Data.SqlClient.SqlParameter("#OFFSET", model.Page));
cmd.Parameters.Add(new System.Data.SqlClient.SqlParameter("#FETCH", model.PageSize));
**Dynamic Where clause** -->> System.Data.SqlClient.SqlParameter("#WhereClause", whereClauseString));
var da = new System.Data.SqlClient.SqlDataAdapter((System.Data.SqlClient.SqlCommand)cmd);
da.Fill(ds);
}
My question is it possible to build a dynamic where clause and pass it to the stored procedure and sort on the columns that are being referenced in the where clause?
How would I know which columns I need in the stored procedure that are being referenced in the where clause?
Would such a thing be possible with this stored Procedure and Entity Framework?
Anyway, if your DB is far from external attacks with SQL Injection you still can think in an easy solution based in Dinamic SQL, very often this way can solve quickly very complex problems otherwise:
CREATE Procedure [dbo].[myStoredProcedure]
#TaskId int = 0
, #FileName varchar(200) =''
, #DataDtFrom smalldatetime = '01/01/1900'
, #DataDtTo smalldatetime = '01/01/1900'
, #OFFSET INT = 0
, #FETCH INT = 2000
, #WhereClauseString varchar(5000) = ''
AS
BEGIN
DECLARE #SQL AS NVARCHAR(MAX);
SELECT #SQL = '
SELECT
DataDt
,EffDt
,LoanNumber
,UploadDate
,UploadedFileName
FROM dbo.myFileTable u
WHERE
('''+CONVERT(CHAR(10), #DataDtTo, 112)+''' = ''01/01/1900'' or DataDt between '''+CONVERT(CHAR(10), #DataDtFrom, 112)+''' and '''+CONVERT(CHAR(10), #DataDtTo, 112)+''')
and ('+CONVERT(CHAR(10), #TaskId)+' = 0 or TaskId = '+CONVERT(CHAR(10), #TaskId)+')
and ('+RTRIM(CONVERT(CHAR(100), #FileName))+' = '''' or UploadedFileName like ''%'+RTRIM(CONVERT(CHAR(100), #FileName))+'%'')
' +CONVERT(CHAR(100), #WhereClauseString)+'
ORDER BY u.UploadDate
OFFSET '+CONVERT(CHAR(10), #OFFSET)+' ROWS
FETCH NEXT '+CONVERT(CHAR(10), #FETCH)+' ROWS ONLY;
'
PRINT #SQL;
EXEC sp_ExecuteSQL #SQL;
END;
I've been developing a stored procedure in order to get a register from a table so I built the following query to achieve this:
ALTER procedure [dbo].[procedure_odd]
#codSeccion int,
#NomDoce varchar(500),
#codMate varchar(500)
as
begin
declare #hora varchar(50);
set #hora = (
select b.man_nomhor
from ra_hpl_horarios_planificacion a
inner join ra_man_grp_hor b on a.hpl_codman = b.man_codigo
where a.hpl_codcil = 100
and a.hpl_codemp = (select emp_codigo from pla_emp_empleado
where emp_nombres_apellidos = #NomDoce)
and a.hpl_codmat = #codMate
and a.hpl_descripcion = #codSeccion)
return #hora
end
I've tested this query (ONLY the query not the stored procedure with the query) in my SQL Server console and it works just fine. The problem is when I call it from C# it doesn't work no matter what I try! Also I tried to develop a stored procedure with output parameter but with no result.
Also I've tried this other way(which works so good and fine!):
select b.man_nomhor
from ra_hpl_horarios_planificacion a
inner join ra_man_grp_hor b on a.hpl_codman = b.man_codigo
where a.hpl_codcil = 100
and a.hpl_codemp =
(select emp_codigo from pla_emp_empleado
where emp_nombres_apellidos = 'julio escobar')
and a.hpl_codmat = 'FONO-I'
and a.hpl_descripcion = 1;
Here is my code on C# (My 11000 solution):
public String horarios(int Seccion, string NomDocent, string CodMate)
{
cn.Open();
cmd.Connection = cn;
cmd.CommandText = "select b.man_nomhor from ra_hpl_horarios_planificacion
a inner join ra_man_grp_hor b on a.hpl_codman = b.man_codigo where
a.hpl_codcil = 100 and a.hpl_codemp =(select emp_codigo from
pla_emp_empleado where emp_nombres_apellidos = '" + NomDocent +
"') and a.hpl_codmat = '" + CodMate + "' and a.hpl_descripcion = '" + Seccion + "'";
dr = cmd.ExecuteReader();
if (dr.HasRows)
{
if (dr.Read())
{
msj = dr[0].ToString();
}
}
cn.Close();
return msj;
}
When I run my Visual Studio it doesn't show any error at all but in the variable MSJ it set an empty STRING like this MSJ = "";
This must be really easy but it just that (believe) I've tried so hard to get to the solution with bno results, please help!
It looks like Seccion (and thus a.hpl_descripcion) are integers, but your query is placing apostrophes around it like a literal.
Try removing the apostrophes:
... + "' and a.hpl_descripcion = " + Seccion;
If that's indeed the issue, parameterizing your query can eliminate these kinds of mistakes:
cmd.CommandText = "... and a.hpl_codemp =(select emp_codigo from pla_emp_empleado where emp_nombres_apellidos = #NomDocent) and a.hpl_codmat = #CodMate and a.hpl_descripcion = #Seccion";
cmd.AddParameterWithValue("#NomDocent", NomDocent);
cmd.AddParameterWithValue("#CodMate", CodMate);
cmd.AddParameterWithValue("#Seccion, Seccion);
dr = cmd.ExecuteReader();
Few things:
if you want this to be a stored procedure, be sure to set command.CommandType = CommandType.StoredProcedure.
you do not need to call return or set a variable to return the text you have. instead, just have a select statement return the one field:
ALTER procedure [dbo].[procedure_odd]
#codSeccion int,
#NomDoce varchar(500),
#codMate varchar(500)
as
begin
select top 1 b.man_nomhor from ra_hpl_horarios_planificacion a inner join
ra_man_grp_hor b on a.hpl_codman = b.man_codigo where a.hpl_codcil = 100
and a.hpl_codemp = (select emp_codigo from pla_emp_empleado
where emp_nombres_apellidos = #NomDoce) and a.hpl_codmat = #codMate and
a.hpl_descripcion = #codSeccion)
END
once this is done, just execute the stored procedure with a dataReader, that will return your value. if you need more information, I can edit my answer to clarify.
I have the following Stored Procedure :
ALTER PROCEDURE [dbo].[ProcedureName]
#date NVARCHAR(50)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #result nvarchar(500) -- this one should return string.
DECLARE #variable1 NVARCHAR(50)
set #variable1 = (SELECT COUNT(*) FROM dbo.Table1 WHERE column1 not in (select column1 from dbo.Table2))
DECLARE #variable2 NVARCHAR(50)
update dbo.Table1 set columnX = 1 where column1 not in (select column1 from dbo.Table2)
set #variable2 = ##ROWCOUNT
and so on... it continues like 200 rows of script with at least 10-12 variables
after that I want to get result like this
'Hello,' +
'Some Text here' +
#date +': ' +
'Explaining text for variable1- ' + #variable1 +
'Updated rows from variable2 - ' + #variable2 +
'some other select count - ' + #variable3 +
'some other update rowcount - '+ #variable4
......
till now i was able to get this with PRINT Statement, but can't take it to variable in my C# code which goes like this:
public void Execute_Click(object sender, EventArgs e)
{
if (MessageBox.Show("Are you sure you want to execute the program?", "Confirm Start", MessageBoxButtons.YesNo, MessageBoxIcon.Question) != DialogResult.No)
{
string connectionString = GetConnectionString(usernamePicker.Text, passwordPicker.Text);
using (SqlConnection connection = new SqlConnection(connectionString))
{
using (SqlCommand cmd = new SqlCommand("dbo.ProcedureName", connection))
{
connection.Open();
cmd.CommandText = "dbo.ProcedureName";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#date", SqlDbType.VarChar).Value = dateTimePicker1.Text;
SqlParameter result = cmd.Parameters.Add("#result", SqlDbType.VarChar);
result.Direction = ParameterDirection.ReturnValue;
cmd.ExecuteScalar();
var resultout = (string)cmd.Parameters["#result"].Value;
connection.Close();
TextMessage.Text = dateTimePicker1.Text;
}
}
}
}
all i get for result is 0 or NULL or etc.
i tried to return value from SQL with PRINT, RETURN, SET, OUTPUT ....... but nothing seems to work. However fetching variable from C# to SQL seems like child-work. Any ideas?
If you want to return the concatenate string as output then at the end of the procedure just write select #result. Make sure that you have concatenated it before this statement.
This will return you the string which you can use in your c# code as a string.
Change your stored procedure to this:
ALTER PROCEDURE [dbo].[ProcedureName]
#date NVARCHAR(50),
#variable1 NVARCHAR(50) output,
#variable2 NVARCHAR(50) output
AS
BEGIN
SET NOCOUNT ON;
DECLARE #result nvarchar(500) -- this one should return string.
set #variable1 = (SELECT COUNT(*) FROM dbo.Table1 WHERE column1 not in (select column1 from dbo.Table2))
update dbo.Table1 set columnX = 1 where column1 not in (select column1 from dbo.Table2)
set #variable2 = ##ROWCOUNT
and modify your code like this:
SqlParameter result1 = cmd.Parameters.Add("#variable1", SqlDbType.VarChar);
result1.Direction = ParameterDirection.ReturnValue;
SqlParameter result2 = cmd.Parameters.Add("#variable2", SqlDbType.VarChar);
result2.Direction = ParameterDirection.ReturnValue;
ok lets say I did this procedure
ALTER PROCEDURE [dbo].[ProcedureName]
#date NVARCHAR(50),
#result nvarchar(500) output
AS
BEGIN
SET NOCOUNT ON;
DECLARE #variable1 NVARCHAR(50)
set #variable1 = (SELECT COUNT(*) FROM dbo.Table1 WHERE column1 not in (select column1 from Table2))
set #result = #variable1 + ' some text '
i want "#result" to be output text from procedure and get it with C#
I'd like to access the list of tables and for each table report number of rows, disk space used, etc. It would be nice to get these details at the database level, too.
How do I do this programmatically?
Jason's answer above is good, but more generally. you're looking at Information Schema. Also Wikipedia's entry:
http://en.wikipedia.org/wiki/Information_schema
You can just open a connection and query the database:
using(var connection = new SqlConnection(connectionString)) {
connection.Open();
using(var command = connection.CreateCommand()) {
command.CommandText = "SELECT * FROM SYS.TABLES";
using(var reader = command.ExecuteReader()) {
while(reader.Read()) {
Console.WriteLine(reader["name"]);
}
}
}
}
You can Google for the query strings for the other information that you want.
Create a SqlConnection to your database and open the connection.
SqlConnection conn = new SqlConnection("Data Source=Servername;Initial Catalog=Marketing;Integrated Security=SSPI");
conn.Open();
Create a SqlCommand and assign the CommandText to the value of SQL you require.
SqlCommand cmd = new SqlCommand("PLACE SQL HERE", conn);
Tables and number of rows:
SELECT
[TableName] = so.name,
[RowCount] = MAX(si.rows)
FROM
sysobjects so,
sysindexes si
WHERE
so.xtype = 'U'
AND
si.id = OBJECT_ID(so.name)
GROUP BY
so.name
ORDER BY
2 DESC
Space used:
EXEC sp_spaceused 'tablename'
This script doesn't include schema names but gets you most of the information you want for the current database. I'm sure you can adapt it into a stored procedure.
SET NOCOUNT ON
GO
DECLARE #tblSpaceUsed TABLE
(
[name] sysname NOT NULL,
[rows] int NOT NULL,
[reserved] nvarchar(50) NOT NULL,
[reservedKB] int NULL,
[data] nvarchar(50) NOT NULL,
[dataKB] int NULL,
[index] nvarchar(50) NOT NULL,
[indexKB] int NULL,
[unused] nvarchar(50) NOT NULL,
[unusedKB] int NULL
)
DECLARE #tableName sysname
DECLARE #tableNames CURSOR
SET #tableNames = CURSOR
FAST_FORWARD
FOR
SELECT DISTINCT
ss.name + '.' + st.name
FROM
sys.tables st
INNER JOIN
sys.schemas ss
ON st.schema_id = ss.schema_id
OPEN #tableNames
FETCH NEXT FROM #tableNames INTO #tableName
WHILE ##FETCH_STATUS = 0
BEGIN
INSERT INTO #tblSpaceUsed ([name], [rows], [reserved], [data], [index], [unused]) EXEC sp_spaceused #tableName
FETCH NEXT FROM #tableNames INTO #tableName
END
CLOSE #tableNames
UPDATE
#tblSpaceUsed
SET
[reservedKB] = CONVERT(int, LEFT([reserved], LEN([reserved]) - 3)),
[dataKB] = CONVERT(int, LEFT([data], LEN([data]) - 3)),
[indexKB] = CONVERT(int, LEFT([index], LEN([index]) - 3)),
[unusedKB] = CONVERT(int, LEFT([unused], LEN([unused]) - 3))
SELECT
*
FROM
#tblSpaceUsed
I am using the following Code to execute the SP of MySql and get the output value. I need to get the output value to my c# after SP is executed. How ? Thanks.
Code :
public static string GetInsertStatement(string DBName, string TblName, string ColName, string ColValue)
{
string strData = "";
MySqlConnection conn = new MySqlConnection(ConfigurationSettings.AppSettings["Con_Admin"]);
MySqlCommand cmd = conn.CreateCommand();
try
{
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.CommandText = "InsGen";
cmd.Parameters.Clear();
cmd.Parameters.Add("in_db", MySqlDbType.VarChar, 20);
cmd.Parameters["in_db"].Value = DBName;
cmd.Parameters.Add("in_table", MySqlDbType.VarChar, 20);
cmd.Parameters["in_table"].Value = TblName;
cmd.Parameters.Add("in_ColumnName", MySqlDbType.VarChar, 20);
cmd.Parameters["in_ColumnName"].Value = ColName;
cmd.Parameters.Add("in_ColumnValue", MySqlDbType.VarChar, 20);
cmd.Parameters["in_ColumnValue"].Value = ColValue;
conn.Open();
cmd.ExecuteNonQuery();
conn.Close();
}
catch (System.Exception e)
{
Console.WriteLine(e.Message);
}
return strData;
}
SP :
DELIMITER $$
DROP PROCEDURE IF EXISTS `InsGen` $$
CREATE DEFINER=`root`#`localhost` PROCEDURE `InsGen`
(
in_db varchar(20),
in_table varchar(20),
in_ColumnName varchar(20),
in_ColumnValue varchar(20)
)
BEGIN
declare Whrs varchar(500);
declare Sels varchar(500);
declare Inserts varchar(2000);
declare tablename varchar(20);
declare ColName varchar(20);
set tablename=in_table;
# Comma separated column names - used for Select
select group_concat(concat('concat(\'"\',','ifnull(',column_name,','''')',',\'"\')'))
INTO #Sels from information_schema.columns where table_schema=in_db and table_name=tablename;
# Comma separated column names - used for Group By
select group_concat('`',column_name,'`')
INTO #Whrs from information_schema.columns where table_schema=in_db and table_name=tablename;
#Main Select Statement for fetching comma separated table values
set #Inserts=concat("select concat('insert into ", in_db,".",tablename," values(',concat_ws(',',",#Sels,"),');')
from ", in_db,".",tablename, " where ", in_ColumnName, " = " , in_ColumnValue, " group by ",#Whrs, ";");
PREPARE Inserts FROM #Inserts;
select Inserts;
EXECUTE Inserts;
END $$
DELIMITER ;
Using output query parameter.
http://msdn.microsoft.com/en-us/library/59x02y99(VS.71).aspx
If I'm not mistaken instead of
cmd.ExecuteNonQuery();
there is an "ExecuteScalar" which will return a Scalar value.
The problem is that you're calling
cmd.ExecuteNonQuery();
That executes without reading back results.
Have a look at the documentation for Command, especially the methods that start with Execute, and decide which one's best for you