The following C# code was working with MySQL server correctly to get the MAX value of a column from a table and at the same time this query adds 1 to the value like this:
SqlDataReader dr = new SqlCommand("SELECT (MAX(Consec) +1) AS NextSampleID FROM Samples", Connection).ExecuteReader();
while (dr.Read())
{ //in case of Maximum value of Consec = 555, the expected result: A556
txtSampleID.Text = "A" + dr["NextSampleID"].ToString();
}
However this code does not work anymore after migrating the DB from MySQL to SQL Server, the result is the same if MAX(Consec) = 555 the result after running the query is A555, it does not add 1 like before when using MySQL server.
Question: What is the correct query to get the MAX value of Consec and how to add "1" to the result of MAX in the same query?
The MySQL query is wrong and won't work except in trivial applications, with only a single user, no deletions and no relations :
Concurrent calls will produce the same MAX value so result in the same, duplicate next value
Deleting records will reduce the MAX value, resulting in previous ID values getting assigned to new rows. If that ID value is used in another table, the new record will end up associated with rows it has no real relation. This can be very bad. Imagine one patient's test samples getting mixed with another's.
Calculating the MAX requires locking the entire table or index, thus blocking or getting blocked by others. Given MySQL's MVCC isolation though, that wouldn't prevent duplicates as concurrent SELECT MAX queries wouldn't block each other.
It's possible MAX+1 would work in a POS application with only one terminal generating invoice numbers, but as soon as you added two POS terminals you'd risk generating duplicate invoices.
In an e-Commerce application on the other hand, it's almost guaranteed that even if only two orders are placed per month, they'll happen at the exact same moment, resulting in duplicates.
Correct MySQL solution and equivalent
The correct solution in MySQL is to use the AUTO_INCREMENT attribute :
CREATE TABLE Samples (
Consec INT NOT NULL AUTO_INCREMENT,
...
);
If you want the invoice number to contain other data, use a calculated column to combine the incrementing number and that other data.
The equivalent in SQL Server is the IDENTITY property :
CREATE TABLE Samples (
Consec INT NOT NULL IDENTITY,
...
);
Sequences
Another option available in SQL Server and other databases is the SEQUENCE object. A SEQUENCE can be used to generate incrementing numbers that aren't tied to a table. It can also be reset, making it ideal for accounting applications where invoice numbers are reset after a specific period (eg every year).
Since a SEQUENCE is an independent object, you can increment and receive the new value before inserting any data in the database with NEXT VALUE FOR eg :
SELECT NEXT VALUE FOR seq_InvoiceNumber;
NEXT VALUE FOR can be uses as a default constraint for a table column the same way IDENTITY or AUTO_INCREMENT are used:
Create MyTable (
...
Consec INT NOT NULL DEFAULT (NEXT VALUE FOR seq_ThatSequence)
)
Multi-table sequences
The same sequence can be used in multiple tables. One case where that's useful is assigning a Document ID to data imported from multiple sources, stored in different tables, eg payments.
Payment providers (credit cards, banks etc) send statements using different formats. Obviously you can't lose any information there so you need to use different tables per provider, but still be able to handle payments the same way no matter where they came from.
If you used an IDENTITY on each table you'd end up with conflicting IDs for payments coming from different providers. On eg the OrderPayments table you'd have to record both the provider name and ID. Generating a single view of payments would end up with ID values that can't be used by themselves.
By using a single SEQUENCE though, each record would get its own ID, no matter the table.
Related
Scenario
I want to generate a unique integer key (not a database primary key) by extracting max() and incrementing it by one. Then I want to use that integer key for insert/update operations in one or more tables. I prefer to do this in C# code but if there's no other option then I could go for SQL batch statements or inside a stored procedure etc as well.
Question
Since multiple users can be doing this at the same time, how do I ensure that no two users get the same max()?
Sample pseudocode
Let's say there are two tables - Employee (Columns: EmpId, BatchId, Name) and MiscData (Columns: EmpId, BatchId).
Below is the C# inspired pseudocode that shows one implementation of this scenario -
void DoOperation(int empId, string[] names)
{
int maxBatchId = repository.GetMaxBatchId(); //GetMaxBatchId() basically executes select max(BatchId) from Employee
maxBatchId++;
foreach(string name in names)
command.ExecuteNonQuery("insert into Employee values (" + maxBatchId + ", '" + name + "')");
command.ExecuteNonQuery("insert into MiscData select EmpId, " + maxBatchId + " where EmpId = " + empId);
}
The method DoOperation above can be doing whatever database operations based on the value of maxBatchId + 1.
If more than one users run DoOperation(...) at the same time, they're likely to get exactly same maxBatchId. How do I ensure that only a single instance of this method can run at one time?
You can use Sequence in SQL Server
CREATE SEQUENCE testseq
START WITH 1
INCREMENT BY 1 ;
SELECT NEXT VALUE FOR testseq
I cannot use sequence in SQL server because I cannot have gaps in the numbers due to regulatory rules.
This is an old post but while I was reading this to find an answer I thought of a different way that might actually work and posting it here to help others.
If you create a new column in the table and set the identity insert to true and 1,1 when you save the record it will be unique.
BUT you may not be able to modify the database table as it may affect other areas...
So what you can do is create a new table with three fields: ID, Username, CurrentDateTime.
The ID field is incrementing(1,1)
When you want a unique ID simply add a record to that table with the current date and time and the username which will generate you a unique ID and you can
Select Top 1 ID where UserName = XXX order by DateTime Desc
That will give you that users last entry in this table which you can use as your unique integer key
Pros:
Unique identifier that will never duplicate in a multi-user
environment
If you have records already created you can insert them
into this table after the fact and start using this method.
Cons: An extra table
The extra table will take almost no space in your database and any old systems won't be affected by this new table. New systems might complain that the table should not exist and of course this needs to be tested.
i have a sql server database with table. These are
1stAP_TB, 2ndAP_TB, 3rdAP_TB, 4thAP_TB, 1steng_TB, 2ndeng_TB, 3rdeng_TB,
4theng_TB
all in them are in row. The numbers will be solve individually on specific column. Now, i need to know how am i going to get the average of 1stAP_TB, 2ndAP_TB, 3rdAP_TB and 4thAP_TB while there are in rows.
Also, there are multiple data that will be save inside the database. I am using C# programming language.
Try below method
create table aveexample
(a1stAP_TB int,
a2ndAP_TB int,
a3rdAP_TB int,
a4thAP_TB int,
a1steng_TB int,
a2ndeng_TB int,
a3rdeng_TB int,
a4theng_TB int
)
Sample data
insert into aveexample values(1,2,3,4,5,6,7,8)
insert into aveexample values(11,22,33,44,55,66,77,78)
insert into aveexample values(2,3,1,4,10,10,45,5)
Method 1
select *, (select AVG(totaldata)
from (values(a1stAP_TB),
(a2ndAP_TB),(a3rdAP_TB),(a4thAP_TB),(a1steng_TB),
(a2ndeng_TB),(a3rdeng_TB),(a4theng_TB)) total(totaldata))as average
from aveexample
Method 2
select ((a1stAP_TB)+
(a2ndAP_TB)+(a3rdAP_TB)+(a4thAP_TB)+(a1steng_TB)+
(a2ndeng_TB)+(a3rdeng_TB)+(a4theng_TB))/8 as Average
from aveexample
It is difficult to give concrete advice given the very limited description in the question, but from the description and comments so far, it seems to me like the database needs to be redesigned to better fit your requirements. First, you have no ID field, so there is no way to differentiate one row from the next. Then, what you are left with is a series of repeated values. The clue here is that you have "1st", "2nd", "3rd" in the column names. That's probably a sign that those columns need to be moved into rows of a related table. It may not instantly seem to be the best approach, but this is called "First Normal Form" and is a typical best practice with SQL databases. See also Database Normalization Basics.
It seems to me that what you have here is some entity (which you haven't mentioned in your question) that has a number of values associated with it. The 'entity' here should be given a unique ID and then all of the values for that entity stored with its ID.
You might have a table with the following columns:
CREATE TABLE MyItems (
ID int NOT NULL,
Sequence int NOT NULL,
Value int NOT NULL,
CONSTRAINT PK_MyValues_ID_Sequence PRIMARY KEY
(ID,Sequence)
)
Note: ID + sequence forms the unique primary key for the table and makes every row unique. This also lets you keep track of what order the items were added in. This may or may not be important to you but every table should probably have a unique primary key.
Your data table would then look something like this (the example represents two different entities, the first having 4 values and the second having 3 values):
It's difficult to show a sensible example without knowing more about the application and what it does... but with this table design you have a basis from which to add values one at a time, as you said you needed, and a way to query them back. You can use grouping to produce things like totals and averages, or you can do that in code by iterating over the results of a query or in a LINQ statement.
You can then compute the average for an entity of a given ID using a LINQ query along the lines of:
var average = MyItems.Where(p=>p.ID == 1).Average(q=>q.Value);
As an example of the flexibility of this sort of approach, you could just as easily compute the average of every second value entered across the entire database:
var averageOfSecondItems = MyItems.Where(p => p.Sequence == 2).Average(q => q.Value);
The example I've shown deals with one type of value. In your question it appears that you might have two different types of value. There are several ways you could handle that - for example you could add another column to the table if the values are always entered in pairs, or you could create a second table to hold the separate values. Again, it's hard to make a recommendation based on the limited information given.
If putting your data into First Normal Form seems like a lot of work, then your application might be a better fit for a document database ("NoSQL" database), but that is really a different question. In the question, a SQL database was specified so I've concentrated on that.
I have noticed that SQL keeps track of the latest identity (field it automatically increments each time a new record is created). I want to retrieve the latest identity from the table using C#. Unfortunately most of the time the table is empty (records are written and removed after a while, so often the table is empty).
Is this possible to do this within the bounds of the C# SQL API or do I have to create a stored procedure to retrieve this?
To better explain. If the row above was removed, the next ID number for the next record would be 32. I want to retrieve 32 before the record is written in, in the situation where the table is empty.
SELECT IDENT_CURRENT('table_name')+1;
IDENT_CURRENT returns the last identity value generated for a specific
table in any session and any scope.
http://msdn.microsoft.com/en-us/library/ms175098.aspx
However, although this shows what the next ID will be, this doesn't always mean it will be the next ID entered by yourself. Someone else could INSERT a new record before you.
In short, there is no way of returning the value of what you will next be inserting.
Just in case the increment is not the regular "1":
SELECT IDENT_CURRENT('mytable') + IDENT_INCR('mytable')
Of course, all these rely on the identity column having been populated consistently, ie no messing with manual inserts or reseeding. And Curt has mentioned the possible concurrency issue if you're going to use this ID for your insert directly.
That would be the following query
DBCC CHECKIDENT ('[TableName]', NORESEED )
I will be having a table in SQL Server 2008 that will hold millions of rows and the initial design will be:
Code nvarchar(50) PK
Available bit
validUntil DateTime
ImportID int
The users can import 100,000 odd codes at a time which I will be using sqlbulkcopy to insert the data. They can also request batches of codes of up to 10,000 at a time for a specific ImportID and as long as the request date is less than the ValidUntil date and the code is available.
My question is, will it be better to hold all these codes in the one table and use indexes or to split the one table into two - AvailableCodes and UsedCodes - so whenever codes are requested, they are moved from the AvailableCodes table into the UsedCodes table instead of having the Available flag? That way the AvailableCodes table won't get as massive as a single table as over time there will be more used codes than available codes and I will not be that bothered about them accept for auditing purposes.
Also, if the tables are split will I still be able to use the sqlbulkcopy as the codes will still need to be unique across both tables?
I would create it in one table and create well defined indexes.
Consider a filter index for the flag column. This is done with a where clause in t-sql and the filter page in ssms.
http://msdn.microsoft.com/en-us/library/cc280372.aspx
I want to get some random records from db. There is two solution for this :
1- Using TABLESAMPLE for getting data from db directly.
2- Write a method In my application for doing this. In this method we generate multiple random number and get data like this :
select * from db where ID = #RandomNumber
if this ID does not exist, I pass a new number.
Now which one has better performance?
According to the documentation for TABESAMPLE you shouldn't use it if you "really want a sample of individual rows":
If you really want a random sample of
individual rows, modify your query to
filter out rows randomly, instead of
using TABLESAMPLE. For example, the
following query uses the NEWID
function to return approximately one
percent of the rows of the
Sales.SalesOrderDetail table:
SELECT *
FROM Sales.SalesOrderDetail
WHERE 0.01 >= CAST(CHECKSUM(NEWID(), SalesOrderID) & 0x7fffffff AS float)
/ CAST (0x7fffffff AS int)
The SalesOrderID column is included in
the CHECKSUM expression so that
NEWID() evaluates once per row to
achieve sampling on a per-row basis.
The expression CAST(CHECKSUM(NEWID(),> SalesOrderID) & 0x7fffffff AS float / CAST(0x7fffffff AS int) evaluates to a random float value between 0 and 1.
Either way, given the potentially endless number of requests you could make by passing in #RandomNumber (in theory the first 1000 requests you make might return nothing), the better approach is to limit the resultset on the server.
try this:
SELECT TOP 1 * FROM db
ORDER BY NEWID()
the NewID function will generate UniqueIdentifier value and it will be random.
Source: SQL to Select a random row from a database table
I would use TABLESAMPLE, as its makes it very easy to generate sample data. I expect it would be more efficient as you only call one piece of SQL.
e.g.
USE AdventureWorks ;
GO
SELECT FirstName, LastName
FROM Person.Contact
TABLESAMPLE (10 PERCENT)
In your other example, you will have to keep on calling select * from db where ID = #RandomNumber many times.
If you after individual rows then i would use another method, some form of random TOP 1 etc...
I recommend to read a post about various methods to get random row from table. It's based on PostgreSQL, but I'm sure that 90% applies to SQL Server too.
Of course most flexible and best performing solution can be achieved by writing a stored procedure.
Cost (hence: best performance) of getting truly random sample depends on data (type of data, statistics and distribution, including sparseness).