C# list items to database SQL - c#

Hello I have a simple question that regards inserting data into a MS MySql Database 2012 table. The table that I have is called COMPLETED and has 3 fields.
student_ID (int, NOT allowed nulls)
completed (bool, NOT allowed nulls)
random_code (string, allowed nulls)
In c# I have a list filled with unique random codes. I want all codes inserted into the database, so if I have 20 records I want 20 unique codes inserted into the random_code field. So the first records gets the first code, the seconds records gets the second code and so on. I think the best way to do this is using a foreach and, for each code in the list of codes insert that code into the random_code field in my database. The problem is I don't know how to do this. I have the following code that give's me an error at VALUE:
Incorrect syntax near 'VALUE'.
foreach (string unRaCo in codes)
{
//insert database
SqlCommand toDB = new SqlCommand("INSERT INTO COMPLETED (random_code) VALUE ( '"+ unRaCo +"' ) ", conn);
SqlDataReader toDBR;
toDBR = toDB.ExecuteReader();
}
Could anyone give me a dircetion here? Thanks in advance.
EDIT:
Okay I totally changed my query as I figured out it did not yet do what I wanted it to do. I now want to update my records instead of inserting records. I did that with the following code:
foreach (string unRaCo in codes)
{
//insert database
SqlCommand naarDB = new SqlCommand("UPDATE VOLTOOID SET random_code = '"+ unRaCo +"' ", connectie);
SqlDataReader naarDBR;
naarDBR = naarDB.ExecuteReader();
naarDBR.Close();
}
The problem this time is that the update query updates ALL records with the first code, so the first record has the code 12345 for example but all other records also have that code. I want to update 12345 into record 1 and 54321 for example in number 2, how do I do that?

The correct is Values not Value, even if you only provide one column.
About your edit. First of all beware of SQL Injection. You better use SQLParameter class. Check Configuring Parameters and Parameter Data Types for further info.
If you want to update a specific id then use a where clause like (in plain SQL):
UPDATE VOLTOOID SET random_code = #NewValue WHERE random_code = #OldValue
Now if you just want to add the random number in a specific row, then you would have to use some more advanced SQL functions. Again in plain SQL you would have:
;WITH MyCTE AS
(
SELECT random_code,
ROW_NUMBER() OVER(ORDER BY random_code) AS ROWSEQ -- This will give a unique row number to each row of your table
FROM VOLTOOID _code
)
UPDATE MyCTE
SET random_code = #NewValue
WHERE ROWSEQ = #YourRandomRow
As the above queries are for SQL script execution you will need to define the variable used.

Your syntax is wrong, you are using 'value' where you should use 'values'. If you have SSMS you will able to easily figure out this kind of errors.
Usually I create the query in SQL Server Management Studio query editor, then use it in C#. Most of the times I use SQL Server stored procedures where it's possible. Because I think it cost some extra resources to execute a text query than executing a procedure

Related

SQL Server CE & C# After deleting records cannot read data in a table

I have created a database file that updates readings every minute and stores in a SQL Server CE database file. However as the database gets very large, it starts to get really slow.
I decided to delete the oldest files once the database reaches a certain size as they are of no use to me. I managed to do this using the following commands:
command.Append("DELETE FROM MyTable WHERE ID IN (SELECT TOP(" + difference.ToString() + ") ID From MyTable)");
where difference.ToString() is value I used to calculate how much I want to delete.
This worked successfully as I could open the file using CompactView and also I could type in the commands in CompactView to give the same results.
Now my problem started when I tried to read the data and update it on to a graph. So my codes in another form does the following:
private void updateGraphTimer_Tick(object sender, EventArgs e)
{
using (SqlCeConnection connection = new SqlCeConnection(connectionString))
{
connection.Open();
using (SqlCeDataAdapter adapter = new SqlCeDataAdapter("SELECT * FROM MyTable", connection))
{
// some code that is not relevant between these two statements
using (DataTable table = new DataTable())
{
StringBuilder command = new StringBuilder();
command.Clear();
command.Append("SELECT TOP(1) ID FROM MyTable ORDER BY ID DESC");
using (SqlCeCommand com = new SqlCeCommand(command.ToString(), connection))
{
int value = (int)com.ExecuteScalar();
graphPage.latestID = value;
if (value > graphPage.startID)
{
DataColumn xDateColumn;
xDateColumn = new DataColumn("XDate");
xDateColumn.DataType = typeof(double);
table.Columns.Add(xDateColumn);
adapter.Fill(graphPage.startID, value, table);
The problem I have is that the table is empty, even though value from (int)com.ExecuteScalar() returns a value! If I did not perform the delete, it all works fine!
I cannot figure out what is happening! The only thing I can think of is something to do with reading and writing the sql file.
Much appreciated!
Better not use TOP here. As ID is incremented automatically, get the highest ID and then delete at max before that ID as the latest ID is used in the graphing part. For example, delete everything below current max ID or below max ID - 10 or so.
DELETE * from table where ID < MAX(ID);
If using TOP as you do in the delete, all values, including the TOP (last ID) is deleted. And in the graphing part you are accessing the last ID too.
So you have two problems:
your table is empty after the delete
your query returns a result in spite of the fact that the table is empty
Regarding the first one the problem is in your delete statement: for some reason your difference value is higher or equal to the number of rows in your table and will delete the overall table.
I would look at the way you are computing it.
In any case if you have an insert_date on your table I would change the delete statement like this to delete rows older than 10 days from today:
DELETE FROM MyTable WHERE ID IN (SELECT ID From MyTable where insert_date < DATEADD(day, -10, GETDATE())
The second problem is a concurrency issue related to the fact that two threads are working: it may happens that you read data (and data are available) just before data are deleted; so you are not really getting a query result from an empty table also if this is the impression.
I think I figured out the problem! Forgive me if you tried to explain this to me and I didn't get it but here goes! The problem is with populating the table whilst the value returned from ExecuteScalar is correct, that Id is greater than the table as the table counts from 0 rather than the subtracted amount!
So for example I deleted 1000 records from 5000, the oldest record would then be 1000, so when I fill the table the 5000 is the latest id, however I am trying to access the 5000th record in the table rather than the 4000th value which contains ID 5000!
I will try and rewrite the code take this into account, this was never a problem before as when I wasn't deleting they matched. D'OH!

Get MAX id number from database table after deleting last record

I want to get a new row id for "products", for this I use MAX SQL command as follwing (the command is in insert new record button click event):
SqlCommand cmd = new SqlCommand("Select ISNULL(MAX(id)+1,0) from products", SqlCon);
the issue is when there are rows with IDs 10,11,12 (12 is MAX) and i delete id 12 record , i gets MAX+1 id 12 when the new id row is 13 ("id" field is PK with identity increment 1).
can i do it with other way?
example:
id prodect
-- -------
1 dog
2 cat
3 mouse
4 elefant
when i deletes row 4 i get MAX(id)+1 = 4 and i want to get 5 since this is the next row id.
I suspect the actual question is How can I find the ID of the row I just inserted so I can use it as a foreign key in related tables or in an image file name?
SQL Server since 2005 provides the OUTPUT clause in INSERT, UPDATE, DELETE statements that returns the values of the columns just inserted or modified. In the case of the insert statement, the syntax is:
insert into Products (Product)
OUTPUT inserted.ID
VALUES ('xxx')
This is a better option than the IDENT_CURRENT or SCOPE_IDENTITY values because it returns the values using a single statement and there is no ambiguity about what is returned:
IDENT_CURRENT may return a different value if multiple users are writing to the table outside a transaction
SCOPE_IDENTITY returns the last ID generated in a transaction, no matter the table
You can return more than one column:
insert into Products (Product)
OUTPUT inserted.ID, inserted.Product
VALUES ('xxx')
You can execute this statement with ExecuteScalar, if you return only one column or ExecuteReader, if you want to return more columns.
In the case of UPDATE or DELETE statements, the deleted table contains the deleted values and inserted contains the new values
Note ORMs like Entity Framework use such statements already to retrieve auto-generated IDs and update saved objects. In this case one only needs to read the ID property of the saved objects.
I will take a stab at what I think you are after. :)
If you include SELECT SCOPE_IDENTITY(); in your SQL you will get the ID you need:
INSERT INTO products (
* your fields *
)
VALUES (
* your values *
);
SELECT SCOPE_IDENTITY();
And then in your code you can have:
var Id = Convert.ToInt32(cmd.ExecuteScalar());
This will give you the id of the record you have inserted.
One possible solution could be that you don't delete the rows. You can add a flag and make it inactive/deleted. That way your row numbers will always be preserved and your code will give you the max Id.
I think the OP tries to tackle the wrong problem...
When you insert a new product into the products table, you should try to retrieve the new id directly with the scope_identity function as such (SQLServer!):
string sql = "insert into products(name) values('Yellow Cup'); SELECT SCOPE_IDENTITY();";
var sqlCommand = new SqlCommand(sql, conn);
var id = cSqlServer.ExecuteScalar();
Definitely MAX is not what anybody would use in this case. Closest solution would be to get recently used identity value and then increment it by 1 (in your case) or by seed value, whatever it is.
select ident_current('products') + 1
Caution - although this solves your purpose for now, beware that 'ident_current' will return you the identity value set by other sessions as well. In simple words, if there is some request/trigger/execution that causes id to be incremented even before your button click finishes then you you will get inserted_id and not deleted one.

Insert 2 rows in tables with foreign key relation without knowing the primary key of the main table

I have the following databasescheme in SQL Server Manager 2014.
I'm making a C#-windows application in Visual Studio and I want to insert a new orderline and a new order. The problem is that the primary keys of both tables, auto-generate in server manager, so I haven't yet the value of the primary key of the order-table, but I need that value to fill into the foreign key of the orderLine column. How can I insert these two rows.
Kind regards
SCOPE_IDENTITY returns the last identity value inserted into an identity column in the same scope. A scope is a module: a stored procedure, trigger, function, or batch. Therefore, two statements are in the same scope if they are in the same stored procedure, function, or batch.
You can use SqlCommand.ExecuteScalar to execute the insert command and retrieve the new ID in one query.
using (var con = new SqlConnection(ConnectionString)) {
int newOrderID;
var cmd = "INSERT INTO Order (column_name) VALUES (#Value) ;SELECT CAST(scope_identity() AS int)";
using (var insertCommand = new SqlCommand(cmd, con)) {
insertCommand.Parameters.AddWithValue("#Value", "bar");
con.Open();
newOrderID = (int)insertCommand.ExecuteScalar();
}
}
This will allow you to catch the last generated OrderId and use it in the Insert Statement for the OrderLine table.
Another option is to use the following SQL code:
string command = "INSERT INTO Order(totalPrice) OUTPUT INSERTED.ID VALUES(#totalPrice)" // this will be a parameter from your code
Then the OrderId can be taken from :
Int32 orderId = (Int32) command.ExecuteScalar();
While scope_id() works fine for single rows, you really should learn to use the output clause as scope_id() is useless for multiple rows inserted with a single sql statement.
See this prior question for a simple example of using the output clause.
Obviously this allows you to retrieve more than just the identity value too.
ADDED
Also useful is the new sequence feature (added for 2012) instead of using identity. If your are coming from other databases this may seem a more natural solution.
Sequence is very useful if you would like to share a single sequence among several tables -- although this is an uncommon design I have used it a few times.
If you're using any form of direct SQL, you need to receive the SCOPE_IDENTITY() value immediately after inserting your order, then use that value to insert your lines.
INSERT INTO Order
SELECT SCOPE_IDENTITY() AS NewId; OR RETURN SCOPE_IDENTITY(); OR DECLARE #OrderId INT = SCOPE_IDENTITY();
INSERT INTO OrderLine
Otherwise, use Entity Framework and it will automatically retrieve your new IDs and assign to dependencies.

Update Top in Visual FoxPro

I'm accessing a Visual FoxPro Table (DBF File) via VFP OleDB Provider in a C# Application.
Is there an equivalent of UPDATE TOP (MS SQL) in VFP?
This is my current Query:
UPDATE HM_LIST
SET
HM_DATE=DATE(2014,5,22) ,
HM_STATION="CM_PC" ,
HM_TIME="17:06" ,
HM_USER="TEST"
WHERE
HM_STATION=''
AND HM_TIME=''
AND HM_USER=''
The problem is, all rows are matching to my parameters.
But I want to update only one of those matching rows.
There is no Primary_Key.
I can't use INSERT.
Table
Use WHERE clause as follows:
WHERE RECNO()=1
With the hint of Oleg I found a workaround for the missing primary key.
But it needs two Querys
First select the Record Number alias RECNO of the matching rows.
SELECT RECNO() FROM table_name WHERE foo=''
Now read the first row of the Result (this is the "id" of the row)
Save it as a variable (int row_id) and put after WHERE Statment of the UPDATE Query only following line : "RECNO() ="+row_id
Example :
var MyOleDBCommand = MyOleDBConnection.CreateCommand();
MyOleDBCommand.CommandText = "SELECT RECNO() FROM table_name WHERE foo=''";
int row_id = -1;
/** Search for some matching rows **/
using(var reader = MyOleDBCommand.ExecuteReader()){
// Check if something was found
if(reader.HasRows){
reader.Read(); // Read only the first row (or use a for-loop if you need more then 1)
row_id = (int)reader.GetDecimal(0);
}
}
/** If a matching row was found **/
if(row_id > -1){
MyOleDBCommand.CommandText = "UPDATE table_name SET foo='bar' WHERE RECNO()="+row_id;
if(MyOleDBCommand.ExecuteNonQuery()>0){
//Successfully Updatet
}}
}
Remarks: RECNO has the Type Decimal, so you have to use GetDecimal(0) (see sample code)
If you run the above query in Foxpro, every row will be updated as you state because of the WHERE condition that you are using.
When you specify " Where column1 = '' ", then every row will be effected. Try specifying a value in the condition such as " Where column1 = 'somevalue' " or " Where EMPTY(column1) "
Generally speaking, this is what primary keys are for. Whether its indexed or not, there should be a single field that uniquely identifies each of your records, and allows you to target an update to that particular record and not the entire set.
UPDATE HM_LIST
SET
HM_DATE=DATE(2014,5,22) ,
HM_STATION="CM_PC" ,
HM_TIME="17:06" ,
HM_USER="TEST"
WHERE
HM_ID = 1
If the field doesn't have a primary key, it's strike #2 on the this was horribly designed and should be abandoned, right after you can't insert new rows. Unless this is a theoretical exercise, there are far better tools to accomplish whatever it is that you're after.
That said, for the particular example this is one of those rare instances where mucking about with SQL actually makes your life harder.
FoxPro at its heart, is not a set-based language like SQL. Rather, it's a specialized language focusing on data operations, using the DBF format. For operations where you don't want to deal with entire sets, and for some reason are still programming in FoxPro, you can most easily accomplish this by embracing the xBase roots and running with it.
SELECT HM_LIST
LOCATE FOR FOR HM_STATION='' AND HM_TIME='' AND HM_USER=''
IF FOUND()
REPLACE IN HM_LIST ;
HM_DATE WITH DATE(2014,5,22) ;
, HM_STATION WITH "CM_PC" ;
, HM_TIME WITH "17:06" ;
, HM_USER WITH "TEST"
ENDIF

C# and SQL - elegant way to generate script for several tables

Im working on database synchronization in my app. It means I have 5 databases, but:
only in first database product could be added/removed/modified
this first database saving information about added/removed/modified product to table (with flag 1/2/3 as add/edit/remove and productID)
so first database generates INSERT script from SELECT, for example:
in my product_changes table (addedRemovedEdited INT, productID INT) I have information:
1, 15 (1 - flag means product with ID = 15 was added), or
2, 15 (2 - flag means product with ID = 15 was edited) etc.
Now using this information I can create script - and there is problem.
At this momment im creating scripts like:
SELECT (col1, col2, col3,...) FROM Product_Category;
string query = "INSERT INTO Table VALUES (#a,#b,#c)...";
SELECT (col1,col2,col3,...) FROM Product_price;
query += "INSERT INTO .......";
And I need to do it foreach tables which contains information about one single products. So for 10 products I'll have 10 * 12 (12 because there is ~12 tables about one product) blocks of code like INSERT INTO Table 1(....); INSERT INTO TABLE2(....).
Problem is also that, all data need to have same ID in every databases - so I'm using ##identity and put it into insert query. It has to be this way, because product with ID = 10 with name 'Keyboard' in mainDB = product with ID = 10 in DB10.
And the question - maybe some of you know any better (becouse that one is not so good) solution how can I create those scripts? Like query, which will take all information from my string[] a = {"Product", "Product_price", "Product_category"} tables and generate INSERT queries but - most important - where I can add ##identity.
#EDIT: I forgot. I found that solution: how i can generate programmatically "insert into" data script file from a database table?
Well, it does generate scripts, but with auto-incremented ID. And I need to add information in right order (as middle tables) for example:
INSERT INTO Product(.....) VALUES (...);
SET #pID = ##identity FROM Product;
INSERT INTO Price (priceID,.....) VALUES (...);
SET #prID = ##identity FROM Price;
INSERT INTO Product_price (priceID, productID,...) VALUES (#prID, #pID)

Categories

Resources