I'm using c# and i have three datatables, all three have an int column named idnum, what i need is to keep all numbers in the column idnum unique in the three tables and the next number will always be the small available. For example:
table a
idnum=1
idnum=3
idnum=4
table b
idnum=2
idnum=7
table c
idnum=8
in this case the next number on a new row in any of the three tables would be number 5 then 6 and then 9.
My question is what would be the best aproach to get the next number?
I don't want to use sql.
thanks
nuno
You'd probably want a fourth table, to hold all the "gap" numbers. Otherwise you would have to check every number starting from 1.
On insert: Find the smallest number in the "gaps" table. Use that number when inserting a new item. If there are no items in the gap table, use Max+1 of the idnums across all tables.
On delete: Put the number that you just retired into the "gaps" table.
If your app is multi-threaded, you'd have to add a lock to make sure that two threads don't grab the same gap.
You're not going to be able to do this automatically; the auto-numbering features built into ADO.NET are all scoped to the individual table.
So, given that you're going to have to code your own method to handle this, what's the best way?
If you were using a database, I'd suggest that you use a fourth table, make the ID column in the three main tables a foreign key in which you stored the fourth table's ID, and synchronize inserting a row into the fourth table with inserting a row into either of the other three. Something like:
INSERT INTO Sequence (DateInserted) VALUES (GETDATE())
INSERT INTO TableA (SequenceID, ... ) VALUES (##SCOPE_IDENTITY(), ...)
But you don't want to use a database, which suggests to me that you don't really care about the persistence of these ID numbers. If they really only need to exist while your application is running, you can just use a static field to store the last used ID, and make a helper class:
public static class SequenceHelper
{
private static int ID;
private static object LockObject = new object();
public static int GetNextID()
{
lock (LockObject)
{
return ID++;
}
}
}
The locking isn't strictly necessary, but there's no harm in making this code thread-safe.
Then you can handle the TableNewRow event on each of your three data tables, e.g.:
DataTable t = MyDataSet["TableA"];
t.TableNewRow += new delegate(object sender, DataTableNewRowEventArgs e)
{
r.Row["ID"] = SequenceHelper.GetNextID();
};
This will insure that whatever method adds a new row to each table - whether it's your code calling NewRow() or a new row being added via a data bound control - each row added will have its ID column set to the next ID.
Related
I need to go through a database finding all text-like fields, checking for a particular set of URLs that might be in these fields and modify it using some regex.
The actual text manipulation part is fine, but since I'm generically going through tables, some of which seem to not have primary keys, I'm wondering how to update these rows once I've read this. I'll give a dummy example below.
foreach(var matchingTable in tables){
foreach(var matchingColumn in columns){
SqlCommand currentCommand = new SqlCommand("select * from #matchingTable;");
currentCommand.Parameters.AddWithValue("#matchingTable", matchingTable);
using (SqlDataReader reader = currentCommand.ExecuteReader())
{
while (reader.Read())
{
if(/* logic to check reader[matchingColumn]*/){
/*edit row to change column matchingColumn, a row which I can't be sure has any uniquely identifying factor*/
}
}
}
}
}
Is it possible to edit this arbitrary row or will I have to change how I'm doing this?
If a table do not have guaranteed unique id then there is no value of a duplicated record. And so if a duplicated record is also updated then no harm will be done.
You can consider all the fields as one composite field and perform the update.
One thing to keep in mind is the duplicate records will also be updated.
This might be easier to solve inside the database. You can write a cursor equivalent to the two for-loops to select the table and column. From there you can write a simple UPDATE/WHERE using your regular expression.
I am trying to read all new rows that are added to the database on a timer.
First I read the entire database and save it to a local data table, but I want to read all new rows that are added to the database. Here is how I'm trying to read new rows:
string accessDB1 = string.Format("SELECT * FROM {0} ORDER BY ID DESC", tableName);
setupaccessDB(accessDB1);
int dTRows = localDataTable.Rows.Count + 1;
localDataTable.Rows.Add();
using (readNext = command.ExecuteReader())
{
while (readNext.Read())
{
for (int xyz = 0; xyz < localDataTable.Columns.Count; xyz++)
{
// Code
}
break;
}
}
If only 1 row is added within the timer then this works fine, but when multiple rows are added this only reads the latest row.
So is there any way I can read all added rows.
I am using OledbDataReader.
Thanks in advance
For most tables the primary key is based an incremental value. This can be a very simple integer that is incremented by one, but it could also be a datetime based guid.
Anyway if you know the id of the last record. You can simple ask for all records that have a 'higher' id. In that way you do get the new records, but what about updated records? If you also want those you might want to use a column that contains a datetime value.
A little bit more trickier are records that are deleted from the database. You can't retrieve those with a basic query. You could solve that by setting a TTL for each record you retrieve from the database much like a cache. When the record is 'expired', you try to retrieve it again.
Some databases like Microsoft SQL Server also provide more advanced options into this regard. You can use query notifications via the broker services or enable change tracking on your database. The last one can even indicate what was the last action per record (insert, update or delete).
Your immediate problem lies here:
while (readNext.Read())
{
doSomething();
break;
}
This is what your loop basically boils down to. That break is going to exit the loop after processing the first item, regardless of how many items there are.
The first item, in this case, will probably be the last one added (as you state it is) since you're sorting by descending ID.
In terms of reading only newly added rows, there are a variety of ways to do it, some which will depend on the DBMS that you're using.
Perhaps the simplest and most portable would be to add an extra column processed which is set to false when a row is first added.
That way, you can simply have a query that looks for those records and, for each, process them and set the column to true.
In fact, you could use triggers to do this (force the flag to false on insertion) which opens up the possibility for doing it with updates as well.
Tracking deletions is a little more difficult but still achievable. You could have a trigger which actually writes the record to a separate table before deleting it so that your processing code has access to those details as well.
The following works
using (readNext = command.ExecuteReader())
{
while (readNext.Read())
{
abc = readNext.FieldCount;
for (int s = 1; s < abc; s++)
{
var nextValue = readNext.GetValue(s);
}
}
}
The For Loop reads the current row and then the While Loop moves onto the next row
I know similar questions have been asked, but I have a rather different scenario here.
I have a SQL Server database which will store TicketNumber and other details. This TicketNumber is generated randomly from a C# program, which is passed to the database and stored there. The TicketNumber must be unique, and can be from 000000000-999999999.
Currently, what I do is: I will do a select statement to query all existing TicketNumber from the database:
Select TicketNumber from SomeTable
After that, I will load all the TicketNumber into a List:
List<int> temp = new List<int>();
//foreach loop to add all numbers to the List
Random random = new Random();
int randomNumber = random.Next(0, 1000000000);
if !(temp.Contain(randomNumber))
//Add this new number to the database
There is no problem with the code above, however, when the dataset get larger, the performance is deteriorating. (I have close to hundred thousand of records now). I'm wondering if there is any more effective way of handling this?
I can do this from either the C# application or the SQL Server side.
This answer assumes you can't change the requirements. If you can use a hi/lo scheme to generate unique IDs which aren't random, that would be better.
I assume you've already set this as a primary key in the database. Given that you've already got the information in the database, there's little sense (IMO) in fetching it to the client as well. That goes double if you've got multiple clients (which seems likely - if not now then in the future).
Instead, just try to insert a record with a random ID. If it works, great! If not, generate a new random number and try again.
After 1000 days, you'll have a million records, so roughly one in a thousand inserts will fail. That's only one a day - unless you've got some hard limit on the insertion time, that seems pretty reasonable to me.
EDIT: I've just thought of another solution, which would take a bunch of storage, but might be quite reasonable otherwise... create a table with two columns:
NaturalID ObfuscatedID
Prepopulate that with a billion rows, which you generate by basically shuffling all the possible ticket IDs. It may take quite a while, but it's a one-off cost.
Now, you can use an auto-incrementing ID for your ticket table, and then either copy the corresponding obfuscated ID into the table as you populate it, or join into it when you need the ticket ID.
You can create a separate table with only one column . Lets just name it UniqueID for now. Populate that column with UniqueID = 000000000-999999999. Everytime you want to generate a random number, do something like
SELECT TOP 1 UniqueID From (Table) WHERE UniqueID NOT IN (SELECT ID FROM (YOUR TABLE))
Code has not been tested but just to show the idea
I have an object whose fields I would like to store in a database. I will be using SQL Server Compact Edition (with Visual C# Express 2010). For the record, I'm fairly new to programming with databases (and databases in general). This program will be used every day to read emails, process the orders inside them, store them, and access them when necessary to help with completing the orders. The list of orders is going to become much to large to store in a List, write to a file, create the List from a file, etc. The problem is that each order contains a list of the items purchased. I am aware that I can serialize the list in binary or XML, and use that data as the field. However, this prevents me from searching/selecting based on that list. For instance, if I wanted to find an order based on what items are in it, or see how many times a particular item has been purchased. Since the list will be of arbitrary size, I can't just create a bunch of fields and fill only the ones I need (which in my opinion, is a bad idea anyway).
While writing this I realized a mistake. If I serialize the list again, I could compare the serialized data to find the same list again (though, this assumes that the same data is serialized the same way each time). However, I'm still prevented from finding any particular item.
So, is there any way to store the list of items, in a fixed number of fields (preferably 1) and still be able to search its contents with a query (I will most likely be using LINQ)?
Edit:
To address what I've gotten so far: first, thanks! I'm starting to piece together what I have to do, but I'm not quite there. The consensus seems to be to have a table for each set of items. Does that mean I'd be creating thousands of tables each month?
After re-reading my question I realize I have to be more clear. As the order comes in, I parse the data and store it in an Order object, which consists of the customer's information, and the list of items. Here is a simplified version of what I'm trying to store:
class Order{
private DateTime date;
private String orderNumber;
private String buyerName;
private List<Item> items;
private String address;
}
class Item{
private String itemCode;
private String description;
private int quantity;
}
So would I have to create a new table for each List I create, or am I missing something?
Update:
This may be a helpful reference for One-to-many relationships if you are new to the subject (especially check out the second and most upvoted answer): Create a one to many relationship using SQL Server
You'll want to create a new table in the database for your line items then create a foreign key relationship to the order table. It will be a one to many relationship. Some thing like the following - obviously you'll need to create a FK relationship, but you get the gist.
Existing
CREATE TABLE Order (
OrderID INT,
PONumber VARCHAR,
ItemsList VARCHAR
)
New
CREATE TABLE Order (
OrderID INT,
PONumber VARCHAR
)
CREATE TABLE LineItem(
LineItemID INT,
Description VARCHAR,
Quantity INT,
SequenceNumber INT,
OrderID INT -- <--- Important one here
)
Note that if you want to create just a simple lookup, the relationship would go the other way.
Instead of storing the list as a field, you can create a separate table that hold it's items. And records in this ListTable will have a field pointing to record ID in your original table. That way you can write various queries afterwards.
This can be solved in several ways, but if you want to be able to query the data directly, you should redesign your database with a seperate table where you store your list data with a reference ID. After all, databases are all about data lists.
In my DataGridView I'am displaying a buch of columns from one table. In this table I have a column which points to item in another table. As you may already guessed, I want to display in the grid in one column some text value from the second table instead of and ItemID.
I could not find a right example on the net how to do this.
Lets assume that I have two tables in databes:
Table Users:
UserID UserName UserWorkplaceID
1 Martin 1
2 John 1
3 Susannah 2
4 Jack 3
Table Workplaces:
WorkplaceID WorkplaceName
1 "Factory"
2 "Grocery"
3 "Airport"
I have one untyped dataset dsUsers, one binding source bsUsers, and two DataAdapters for filling dataset (daUsers, daWorkplaces).
Code which I am performing:
daUsers.Fill(dsUsers);
daWorkplaces.Fill(dsUsers);
bsUsers.DataSource = dsUsers.Tables[0];
dgvUsers.DataSource = bsUsers;
At this point I see in my dgvUsers three columns, UserID, UserName and UserWorkplaceID. However, instead of UserWorkplaceID and values 1,2,3 I would like to see "Factory", "Grocery" and so on...
So I've added another column to dgvUsers called "WorkplaceName" and in my code I am trying to bind it to the newly created relation:
dsUsers.Relations.Add("UsersWorkplaces", dsUsers.Tables[1].Columns["WorkplaceID"], dsUsers.Tables[0].Columns["UserWorkplaceID"]);
WorkplaceName.DataPropertyName = "UsersWorkplaces.WorkplaceName";
Unfortunately that doesn't work. Relation is created without errors but fields in this column are empty after running the program.
What I am doing wrong?
I would like to also ask about an example with LookUp combobox in DataGridView which allow me to change the UserWorkplaceID but instead of numeric value it will show a tex value which is under WorkplaceName.
Thanks for your time.
In my opinion, the best decision would be to use the DataGridViewComboBoxColumn column type. If you do it, you should create a data adapter with lookup data beforehand and then set DataSource, DataPropertyName, DisplayMember, and ValueMember properties of the DataGridViewComboBoxColumn. You could also set the DisplayStyle property to Nothing to make the column look like a common data column. That's it.
I don't know if you can do exactly what you want, which seems to be binding the DataGridView to two different DataTable instances simulataneously. I don't think the DataGridView class supports that -- or if it does it's a ninja-style move I haven't seen.
Per MSDN, your best bet is probably using the CellFormatting event on the DataGridView and check for when the cell being formatted is in the lookup column, then you could substitute your value from the other table. Use an unbound column for the WorkplaceName column, hide the UserWorkplaceID column and then implement the CellFormatting event handle to look up the value in the row, e.g.:
private void dgv_CellFormatting(object sender,
DataGridViewCellFormattingEventArgs e)
{
if (dgv.Columns[e.ColumnIndex].Name.Equals("WorkplaceName")
{
// Use helper method to get the string from lookup table
e.Value = GetWorkplaceNameLookupValue(
dataGridViewScanDetails.Rows[e.RowIndex].Cells["UserWorkplaceID"].Value);
}
}
If you've got a lot of rows visible, this might impact performance but is probably a decent way to get it working.
If this doesn't appeal to you, maybe use the DataTable.Merge() method to merge your lookup table into your main table. A quick glance at one of my ADO.NET books suggests this should work, although I have not tried it. But I'm not sure if this is too close to the idea suggested previously which you shot down.
As for your second question about the lookup combobox, you should really post it in a separate question so it gets proper attention.
You could make SQL do the job instead. Use a join to return a table with Workplace names instead of IDs, output that table into a dataset and use it instead.
eg.
SELECT A.UserID, A.UserName, B.WorkplaceID
FROM Users A
JOIN Workplaces B ON A.UserWorkplaceID = B.WorkplaceID
Then use its output to fill dsUsers.