How get huge data from sql server using ado.net? - c#

I'm getting error while fetching the data
Exception of type 'System.OutOfMemoryException' was thrown
Error getting line is dataAdapter.Fill(dataTable);
(2826000) records count in my table.
here is the code i'm using.
var dataTable = new DataTable();
var DicTableNameAndValues = new Dictionary<string, object>();
using (var connection = new SqlConnection(ConnectionString))
{
connection.Open();
var dataQuery = "SELECT * FROM " + table;
using (var command = new SqlCommand(dataQuery, connection))
{
var dataAdapter = new SqlDataAdapter(command);
dataAdapter.Fill(dataTable);
var result = dataTable.AsEnumerable().Skip(skip).Take(pageSize).ToList().Select(c => c.ItemArray);
DicTableNameAndValues.Add(table, result);
}
}

You can get a certain number of records as required by your application instead of all the records. You can create a stored procedure something to the effect of the following.
--Create a stored procedure with following parameters
DECLARE #skip int, #pagesize int
--Added testing values
SELECT #skip = 4, #pagesize = 3
--Give #tbl with your table name.
/*If you already have an identity key then probably row_number function might not
be required. But if the records are getting deleted as well then row_number is a
better option*/
SELECT * FROM
(SELECT ROW_NUMBER() over(ORDER BY Your_Col) AS ROWNUM, * FROM #tbl) as tbl
WHERE ROWNUM BETWEEN (#skip * #pagesize) + 1 and (#skip + 1) * (#pagesize)
Hope this helps.

Related

convert stamp to varchar and use it in c#

guys i created stored procedure to synchronize my data in a table from the main database to the branches using C# service using a last stamp. in the stored procedure i converted the stamp of the main database to varchar select #CurrentStamp = convert(varchar, value) from parameter where id = 3 this stamp i should get it in the C# as an output and send it as parameter to the stored procedure in the branch save the data that should i synchronize then save this stamp in a parameter table as varchar. all by testing without using the soap service. when i tried to use it in the soap service it throws an exception hexadecimal value 0x00, is an invalid character . any help please i tried to convert it to varbinary but it keep changing the stamp to another value. if any one knows how to let the XML read this values without exception please help.
FamilyService.FamilyService fs = new FamilyService.FamilyService();
DataSet ds = fs.GetFamilyToSynchronize(Current.LoggedUserSchool.ID, stamp, out currentStamp);
-
public static DataSet GetFamilyToSynchronize(int schoolID, string stamp, out string CurrentStamp)
{
DataTable dtFamilyToSynchronize = new DataTable();
DataTable dtFamilyPhones = new DataTable();
CurrentStamp = "";
DataSet ds = new DataSet();
try
{
using (DataBaseClass db = new DataBaseClass())
{
db.Connect();
SqlCommand command = db.CreateCommand();
command.CommandType = CommandType.StoredProcedure;
command.CommandText = "GetFamilyToSynchronize";
command.Parameters.AddWithValue("#SchoolID", schoolID);
command.Parameters.AddWithValue("#Stamp", stamp);
command.Parameters.AddWithValue("#CurrentStamp", CurrentStamp);
command.Parameters["#CurrentStamp"].Direction = ParameterDirection.InputOutput;
command.Parameters["#CurrentStamp"].Size = 255;
SqlDataAdapter da = new SqlDataAdapter(command);
da.Fill(ds);
CurrentStamp = command.Parameters["#CurrentStamp"].Value.ToString();
}
}
catch (Exception ex){Logger.LogException(ex);}
return ds;
}
SQL Stored Procedure
GO
ALTER Proc [dbo].[GetFamilyToSynchronize] (#SchoolID int , #Stamp varchar(255) = null, #CurrentStamp varchar(255) output) as
select #CurrentStamp = convert(varchar, value) from parameter where id = 3
SELECT f.*
into #tmpFamily
FROM [Family] f
inner join fatherschools fs on fs.FamilyID = f.ID
where f.id =f.fatherlinkid --not in (select FatherLinkID from family )
and schoolID = #SchoolID
and (#Stamp is null or f.stamp> convert(timestamp, isnull(#Stamp,'')))
select distinct f.id, phonetypeid,phonenumber, t.FatherLinkID
from familyphones f
inner join #tmpFamily t on t.ID = f.FamilyID
select * from #tmpFamily

SQL Resultset returns a table name with * operator and not when specifying columns

I have a stored procedure that selects certain columns into a #table. I then do a select * from that #table in order to retrieve my results.
My problem is that when I use SELECT * INTO #tableName it returns the #table name and the results, but when I SELECT DoodleName INTO #tableName it returns a blank table name.
I want to use the table name in my code.
This is my C# code:
DataTable dataTable = new DataTable();
using (SqlCommand command = new SqlCommand("doodle", connection))
{
command.CommandType = CommandType.StoredProcedure;
try
{
if (connection.State == ConnectionState.Closed)
openConnectionString(connection);
SqlDataReader reader = command.ExecuteReader();
dataTable.Load(reader);
reader.Close();
}
catch (Exception ex)
{
userConnectionErrorMessage = "Error Retrieving results. " + ex.Message;
}
finally
{
if (connection.State == ConnectionState.Open)
connection.Close();
}
Here is the stored procedure that returns the #table name:
SELECT *
INTO [#table]
FROM tblDoodle d
RIGHT OUTER JOIN tblRandom r
ON d.DoodleID = d.DoodleID
WHERE r.RandomID LIKE 1
AND d.Active LIKE 0;
SELECT *
FROM #table;
and here is the stored procedure that does not return the #table name:
SELECT d.DoodleName
INTO [#table]
FROM tblDoodle d
RIGHT OUTER JOIN tblRandom r
ON d.DoodleID = d.DoodleID
WHERE r.RandomID LIKE 1
AND d.Active LIKE 0;
SELECT *
FROM #table;
I have no idea how to get this working. I also think it is an interesting enough question. I can probably use a workaround to get this done, but this is bothering me way more than it should.

copy all rows of a table to another table

I have two databases in MySQL and SQL Server, and I want to create tables in SQL Server and copy all rows from the table in MySQL into the new table in SQL Server.
I can create table in SQL Server same as MySQL, with this code:
List<String> TableNames = new List<string>();
{
IDataReader reader=
ExecuteReader("SELECT Table_Name FROM information_schema.tables WHERE table_name LIKE 'mavara%'",MySql);
while (reader.Read()) {
TableNames.Add(reader[0].ToString());
}
reader.Close();
}
foreach (string TableName in TableNames) {
IDataReader reader =
ExecuteReader("SELECT Column_Name,IS_NULLABLE,DATA_TYPE FROM information_schema.columns where TABLE_Name='" + TableName + "'",MySql);
List<string[]> Columns = new List<string[]>();
while (reader.Read()) {
string[] column = new string[3];
column[0] = reader[0].ToString();
column[1] = reader[1].ToString();
column[2] = reader[2].ToString();
Columns.Add(column);
}
reader.Close();
// create table
string queryCreatTables= "CREATE TABLE [dbo].[" + TableName + "](\n";
foreach(string[] cols in Columns)
{
queryCreatTables +="["+ cols[0] + "] " + cols[2] + " ";
if (cols[1] == "NO")
queryCreatTables += "NOT NULL";
// else
// queryCreatTables += "NULL";
queryCreatTables += " ,\n ";
}
queryCreatTables += ")";
System.Data.SqlClient.SqlCommand smd =
new System.Data.SqlClient.SqlCommand(queryCreatTables, MsSql);
System.Data.SqlClient.SqlDataReader sreader = smd.ExecuteReader();
sreader.Close();
but I have problem to copy rows from one table into another table.
for select query, I use Idatareader, but I don't know how insert rows to another table.
For inserting rows from one table into another table please refer the below sample query
INSERT INTO Store_Information (store_name, Sales, Date)
SELECT store_name, sum(Sales), Date
FROM Sales_Information
The algorithm is as follows:
1. For each table in source database
2. Get a list of columns for that table
3. Create table in destination database
4. SELECT * FROM the table in source
5. For each row in data
6. Generate INSERT statement and execute on destination database
The information you need for a column is Name, Type, Length, etc.
Then you generate the insert statement by iterating on the columns
var insertStatement = "INSERT INTO " + tableName + " VALUES( ";
foreach( var column in columns )
insertStatement += "#" + column.Name + ",";
insertStatement[insertStatement.Length-1] = ')';
var command = new SqlCommand( insertStatement, MsSql );
// iterate over the columns again, but this time set values to the parameters
foreach( var column in columns )
command.Parameters.AddWithValue( "#"+column.Name, currentRow[column.Name] );
But I have a problem to copy rows from one table into another table.
You can use the SqlDataAdapter.UpdateBatchSize to perform Batch Updates/Inserts with a DataAdapter against the database to copy the data from one table to the other. After you get all records from the first MYSQL table using something like:
//Get the rows from the first mysql table as you did in your question
DataTable mysqlfirstTableRowsTobeCopied = GetDataTableFromMySQLTable();
Then you have to create a commend text that do the INSERT something like:
cmd.CommandText = "INSERT INTO TableName Column_Name, ... VALUES(...)";
Or you can use a stored procedure:
CREATE PROCEDURE sp_BatchInsert ( #ColumnName VARCHAR(20), ... )
AS
BEGIN
INSERT INTO TableName VALUES ( #ColumnNamne, ...);
END
Then:
DataTable mysqlfirstTableRowsTobeCopied = GetDataTableFromMySQLTable();
SqlConnection conn = new SqlConnection("Your connection String");
SqlCommand cmd = new SqlCommand("sp_BatchInsert", conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.UpdatedRowSource = UpdateRowSource.None;
// Set the Parameter with appropriate Source Column Name
cmd.Parameters.Add("#ColumnName", SqlDbType.Varchar, 50,
mysqlfirstTableRowsTobeCopied.Columns[0].ColumnName);
...
SqlDataAdapter adpt = new SqlDataAdapter();
adpt.InsertCommand = cmd;
// Specify the number of records to be Inserted/Updated in one go. Default is 1.
adpt.UpdateBatchSize = 20;
conn.Open();
int recordsInserted = adpt.Update(mysqlfirstTableRowsTobeCopied);
conn.Close();
This code actually is quoted from a full tutorial about this subject in the codeproject, you can refer to it for more information:
Multiple Ways to do Multiple Inserts
Assuming that you already have a datatable with rows that you want to merge with another table...
There are two ways to do this...again, assuming you've already selected the data and put it into a new table.
oldTable.Load(newTable.CreateDataReader());
oldTable.Merge(newTable);
Usually that's sth. you can do in SQL directly: INSERT INTO table FROM SELECT * FROM othertable;
For insertion of all record into new table(If the second table is not exist)
Select * into New_Table_Name from Old_Table_Name;
For insertion of all records into Second Table(If second table is exist But the table structure should be same)
Insert into Second_Table_Name from(Select * from First_Table_Name);
Just in case it can help someone, I've found an easy way to do it in C# using SqlDataAdapter. It automatically detects the structure of the table so you don't have to enumerate all the columns or worry about table structure changing. Then bulk inserts the data in the destination table.
As long as the two tables have the same structure, this will work. (For example, copying from a production table to a dev empty table.)
using(SqlConnection sqlConn = new SqlConnection(connectionStringFromDatabase))
{
sqlConn.Open();
using(var adapter = new SqlDataAdapter(String.Format("SELECT * FROM [{0}].[{1}]", schemaName, tableName), sqlConn))
{
adapter.Fill(table);
};
}
using(SqlConnection sqlConn = new SqlConnection(connectionStringDestination))
{
sqlConn.Open();
// perform bulk insert
using(var bulk = new SqlBulkCopy(sqlConn, SqlBulkCopyOptions.KeepIdentity|SqlBulkCopyOptions.KeepNulls, null))
{
foreach(DataColumn column in table.Columns)
{
bulk.ColumnMappings.Add(column.ColumnName, column.ColumnName);
}
bulk.DestinationTableName = String.Format("[{0}].[{1}]", schemaName, tableName);
bulk.WriteToServer(table);
}
}

asp.net C# sql query dependant on session data

I have a Session, which is list int, and I need to make a query that will take from a database only those rows that have the PK value that exists in Session.
I was thinking of doing it with the IN function, or making a new datatable with 1 collumn and values from the Session and doing a double join, probably left...
I just dont know how to make a table from a list.
What I have so far:
String ConnString = "Data Source=BRACO-PC\SQL1;Initial Catalog=DiplomskiSQL1SQL;Integrated Security=True";
SqlConnection Conn = new SqlConnection(ConnString);
Conn.Open();
DataTable ukosarici = new DataTable();
SqlDataAdapter da = new SqlDataAdapter("Select Proizvodi.ime, TipProizvoda.tip, Proizvodi.dimenzije, Proizvodi.cijena from Proizvod LEFT JOIN TipProizvoda On Proizvod.tip=TipProizvoda.id_t WHERE Proizvod.id_p IN ", Conn);
SqlCommandBuilder cmd = new SqlCommandBuilder(da);
da.Fill(ukosarici);
GridView1.DataSource = ukosarici;
GridView1.DataBind();
Conn.Close();
Create a temporary table or table variable, insert the ints into it using INSERT or BULK INSERT, do a join in the SQL query then drop the temp table or table variable.
There are many ways you could do this, but one of my preferred methods is to serialize the list to a CSV, e.g. '1,3,5,33'. I then use a custom SQL Table function to de-serialize the list and filter in the database:
SELECT * FROM mytable t
JOIN dbo.ufn_CSVtoTextList('1,3,5,33' , ',') csv
ON csv.[Entry] = t.Id
The ufn_CSVtoTextList function CREATE script is below:
CREATE FUNCTION [dbo].[ufn_CSVToTextlist] ( #StringInput nVARCHAR(max) ,#SepChar nchar(1) = ',')
RETURNS #OutputTable TABLE ( [Entry] nVarchar(255), [index] int identity (0,1) )
AS
BEGIN
DECLARE #Entry nVarChar(255)
WHILE LEN(#StringInput) > 0
BEGIN
SET #Entry = LEFT(#StringInput,
ISNULL(NULLIF(CHARINDEX(#SepChar, #StringInput) - 1, -1),
LEN(#StringInput)))
SET #StringInput = SUBSTRING(#StringInput,
ISNULL(NULLIF(CHARINDEX(#SepChar, #StringInput), 0),
LEN(#StringInput)) + 1, LEN(#StringInput))
INSERT INTO #OutputTable ( [Entry] )
VALUES ( #Entry )
END
RETURN
END
Try by changing your SqlDataAdapter Call as follows
List<int> list ; // Assign with your session int list values
List<string> l2 = list.ConvertAll<string>(delegate(int i) { return i.ToString(); });
string query = "Select Proizvodi.ime, TipProizvoda.tip, Proizvodi.dimenzije, Proizvodi.cijena from Proizvod LEFT JOIN TipProizvoda On Proizvod.tip=TipProizvoda.id_t WHERE Proizvod.id_p IN (";
query = query + string.Join(",", l2.ToArray()) + ")";
SqlDataAdapter da = new SqlDataAdapter(query, Conn);

Get affected rows on ExecuteNonQuery

I am currently working on a C# project and I am running an insert query which also does a select at the same time, e.g.:
INSERT INTO table (SELECT * FROM table WHERE column=date)
Is there a way I can see how many rows were inserted during this query?
ExecuteNonQuery - returns the number of rows affected.
SqlCommand comm;
// other codes
int numberOfRecords = comm.ExecuteNonQuery();
If you run the SQL from your question in a SqlCommand and check the return value of ExecuteNonQuery it should tell you how many records were affected.
From the documentation:
Return Value
Type: System.Int32
The number of rows affected.
Be sure of one thing also
You need to add a statement in the connection string
For example:
string const "Server=localhost; PORT=3306; Database=db; User id=root; password='';UseAffectedRows=True";
MySqlConnection con = new MySqlConnection(const);
con.Open();
MySqlCommand cmd = new MySqlCommand(con);
cmd.CommandText = "Update db set table = value where Column = value";
int numberOfRecords = cmd.ExecuteNonQuery();
Be sure of:
UseAffectedRows=True
so it will return a right value of rows affected
ExecuteNonQuery return the affected rows ONLY WHEN Use Affected Rows in the connections properties is set, if not (default) returns matched rows.
If you run a bulk of ExecuteNonQuery(), and commit them all in once, you can get the number of total changes after connection by read the return value from "SELECT total_changes();"
The function to get the total changes:
public static long GetTotalChanges(SQLiteConnection m_dbConnection)
{
string sql = "SELECT total_changes();";
using (SQLiteCommand command = new SQLiteCommand(sql, m_dbConnection))
{
using (SQLiteDataReader reader = command.ExecuteReader())
{
reader.Read();
return (long)reader[0];
}
}
}
Use it in another function:
public static long MyBulkInserts()
{
using (SQLiteConnection m_dbConnection = new SQLiteConnection())
{
m_dbConnection.Open();
using (var cmd = new SQLiteCommand(m_dbConnection))
{
using (var transaction = m_dbConnection.BeginTransaction())
{
//loop of bulk inserts
{
cmd.ExecuteNonQuery();
}
transaction.Commit();
}
}
return GetTotalChanges(m_dbConnection);
}
}
I realize you are trying to do this with the ExecuteNonquery, but what about ExecuteScalar and using the OUTPUT directive in your query?
For Insert:
declare #resulttable
(
rowid int
)
insert yourtable
output inserted.rowid
into #resulttable
select *
from someothertable
select count(1) affectedrows
from #resulttable
or for Update, if you only want to know the rows that changed
declare #resulttable
(
beforefield1 varchar(255),
afterfield1 varchar(255)
)
update tbl1
set field1 = replace(field1, 'oldstring', 'newstring')
output deleted.field1,
inserted.field1
into #resulttable
from someothertable
select count(1) affectedrows
from #resulttable
where beforefield1 != afterfield1;

Categories