Get the last inserted string Id in SQL Server & C# - c#

I have a DataTable BonLivraison with a primary key that is a string with the format 2016/ + increment number. I used this code to generate this primary key:
SqlCommand cmdRow = new SqlCommand("select TOP(1) CodeBonLivraison from BonLivraison ORDER BY 1 DESC", con);
string LastCode = (string)cmdRow.ExecuteScalar();
string getID = LastCode.Split('/')[1];
int getIntID = Convert.ToInt32(getID);
numeroBonReceptionTextBox1.Text = DateTime.Now.Year.ToString() + "/" + (getIntID + 1).ToString();
This code generates primary keys normally 2016/1...2016/2....until 2016/10 it re-generates 2016/10 which is primary key violation.
So how modify this code to get last inserted key and get 2016/11...2016/12...

change your query to
select TOP(1) CodeBonLivraison
from BonLivraison
ORDER BY convert(int,
right(CodeBonLivraison, charindex('/', reverse(CodeBonLivraison )) - 1)
) DESC

Try something like this:
select TOP(1) CodeBonLivraison
from BonLivraison
ORDER BY CAST(SUBSTRING(CodeBonLivraison, 6, LEN(CodeBonLivraison ) - 5) as int) DESC

Related

Safely query SQL table with variable table name

I'm trying to make some common code for retrieving identities from tables and that involves making an unsafe query string to inject the table name.
I read everywhere that I cannot safely inject the table name. So I want to query if the table exists, then based on the result, perform a real or dummy query.
var unsafeTableQuery = "SELECT [Id] FROM [dbo].[" + tableName + "] WHERE [BulkInsertSessionID] = #bulkInsertSessionId";
var guardQuery =
"DECLARE #Exists BIT = ( SELECT CAST( COUNT(1) AS BIT ) FROM sys.tables WHERE name = #TableName AND type = 'U' );" +
"IF (#Exists = 0) SELECT TOP 0 NULL 'Id'" +
"ELSE " + unsafeTableQuery;
var cmd = new SqlCommand(guardQuery, conn, tran);
cmd.Parameters.Add(new SqlParameter("#TableName", tableName));
cmd.Parameters.Add(new SqlParameter("#bulkInsertSessionId", bulkInsertSessionId));
using (SqlDataReader reader = cmd.ExecuteReader())
{
int index = 0;
while (reader.Read())
{
int id = (int)reader[0];
entities[index++].Id = id;
}
}
Even though I have an unsafe concatenation, I'm first querying the table name against the sys.tables by a parameter. And if it doesn't exist, the IF..ELSE block will never step into the unsafe query.
For easier readability I'm expecting to run the following query:
DECLARE #Exists BIT = ( SELECT CAST( COUNT(1) AS BIT ) FROM sys.tables WHERE name = #TableName AND type = 'U' );
IF(#Exists = 0)
SELECT TOP 0 NULL 'Id'
ELSE
SELECT [Id] from <InjectedTableName> where BulkInsertSessionID = #bulkSessionId
Am I correct in my assumption that this is safe?
Suppose your users have an access to change the variable tableName. I suppose some user types it on some form. Suppose he types this:
Users]; DROP TABLE Users;--
Then your whole command will be:
DECLARE #Exists BIT = ( SELECT CAST( COUNT(1) AS BIT ) FROM sys.tables WHERE name = #TableName AND type = 'U' );
IF(#Exists = 0)
SELECT TOP 0 NULL 'Id'
ELSE
SELECT [Id] from [Users]; DROP TABLE Users;-- where BulkInsertSessionID = #bulkSessionId
This will do its IF ELSE part and then will go to next statement which is:
DROP TABLE Users;
Note that drop statement will execute in any case even if ELSE part is not executed, because you don't have BEGIN END. Note that the rest is commented out... This is most basic injection method...

using C# for creating a stored procedure

I want to define one stored procedure having 2 tables that insert into the first table and updates the second table. The first table contains the PostID and the PersonID, the second table contains postID and Counter. Would you please help me correct this one? I know this is not correct.
com.CommandText = #"CREATE PROCEDURE dbo.Facebook #PostID int,#PerosnalID int,
BEGIN
INSERT dbo.Like (PostID) VALUES (#PersonalID),
UPDATE dbo.Counter (Counter)
SET PostID = #value1
WHERE Counter = Sum #PersonalID
END";
Let's start and fix the procedure syntax:
com.CommandText = "CREATE PROCEDURE dbo.Facebook( #PostID int,#PersonalID int) AS "+
" INSERT dbo.[Like] (PostID) VALUES (#PersonalID); " +
" UPDATE C SET Counter = (SELECT COUNT(*) FROM dbo.[Like] WHERE PostId = #PersonalID)" +
" FROM dbo.Counter AS C " +
" WHERE C.PostID = #PersonalID ";

Searching multiple SQL tables for data

In my SQL database I have a table for every state in the USA. The user can create multiple pages in different states. Each table has a "UserID" column that will always match the logged in user. How do I search multiple tables for the "UserID"?
string sqlquery = "SELECT * FROM[all tables] WHERE #UserID ='" + userID.Text + "'";
SqlConnection conn1 = new SqlConnection(connString);
SqlCommand comm1 = new SqlCommand(sqlquery, conn1);
SqlDataReader DR1 = comm1.ExecuteReader();
if (DR1.Read())
{
Textbox1.Text = DR1.GetValue(0).ToString();
}
I haven't seen your database schema, but I can already tell you need to refactor it. There's no need to have a table for each state, and the maintenance on that could be rough. What you should have is a table holding all of the states, and then another table with a reference to the State table that holds whatever information you want (your "pages")
CREATE TABLE States
(
StateID int not null, --PK
StateName nvarchar(32) not null
)
CREATE TABLE Pages
(
PagesID int not null, --PK
StateID int not null, --FK
UserID int not null
//Whatever other columns you need
)
Now, you can query the Pages table based on a specific Page, State or User
SELECT * FROM Pages WHERE UserID = (userId)
SELECT * FROM Pages WHERE StateID IN (1, 5, 20)
SELECT * FROM Pages WHERE PageID = (pageID)
The right answer is that you need to change your database so that all this information is held in a single table. The easy answer is to take a few minutes to create a view that unions all the tables together so that you can access them as if they were all in one table.
The only difference in my answer from the others though is that I wouldn't union them in your c# code. I would just create an actual view on the database that unions them all together. The view will be simple (though long) and your code will be simple.
If tables have the same columns I would go for something like. Using the sql UNION.
var statesTables = new[]{"NY, Texas, Oregano, Alabama}";
var qBuild = new StringBuilder();
qBuild.Append(string.Format("SELECT * FROM {0} WHERE UserId = #userId ", statesTables[0]));
for(int i=1;i<stateTables.Length;i++){
qbuild.Append(string.Format("UNION SELECT * FROM {0} WHERE UserId = #userId ", statesTables[i]))
}
SqlConnection conn1 = new SqlConnection(connString);
SqlCommand comm1 = new SqlCommand(qBuild.ToString(), conn1);
comm1.Parameters.Add(new SqlParameter("userId", userId));
It will generate SQL:
SELECT * FROM NY WHERE UserId = #userId
UNION
SELECT * FROM Texas WHERE UserId = #userId
UNION
SELECT * FROM Oregano WHERE UserId = #userId
UNION
SELECT * FROM Alabama WHERE UserId = #userId
If there are some different columns, replace * with column names tables have in common.
BUT, as other suggests, refactoring your DB schema would be the best!

INSERT INTO if not exists SQL server

I have a database structured as follows:
users
userid (Primary Key)
username
group
groupid (PK)
groupName
user_groups
userid (Foreign Key)
groupid (Foreign Key)
The first time a user logs in I would like their info to be added to the users table. So essentially the logic I would like to have if
if (//users table does not contain username)
{
INSERT INTO users VALUES (username);
}
How can I do this intelligently using SQL Server/C# ?
Or using the new MERGE syntax:
merge into users u
using (
select 'username' as uname
) t on t.uname = u.username
when not matched then
insert (username) values (t.uname);
Basically you can do it like this:
IF NOT EXISTS (SELECT * FROM USER WHERE username = #username)
INSERT INTO users (username) VALUES (#username)
But seriously, how you're going to know if user visited your website for the first time?
You have to insert records in table user, when somebody register on your website, not login.
IF NOT EXISTS (select * from users where username = 'username')
BEGIN
INSERT INTO ...
END
I would first create a stored proc on the db to do the check and insert if necessary:
CREATE PROCEDURE AddNewUserProc
(
#username VarChar(50) -- replace with your datatype/size
)
AS
IF NOT EXISTS (SELECT * FROM users WHERE username = #username)
BEGIN
INSERT INTO users
VALUES (#username)
END
Then a method on the app that will call this procedure
public void AddNewUserMethod(string userName)
{
SqlConnection connection = new SqlConnection("connection string");
SqlCommand command = new SqlCommand("AddNewUserProc", connection);
command.CommandType = CommandType.StoredProcedure;
command.Parameters.Add("username", SqlDbType.VarChar, 50).Value = userName;
try
{
connection.Open();
command.ExecuteNonQuery();
}
finally
{
if (connection.State == ConnectionState.Open) { connection.Close(); }
}
}
Note leaving this as alternative/historical, but for purpose of correctness the correct way is using the Merge statement, see answer https://stackoverflow.com/a/9649040/167304 or checkout MS doc https://learn.microsoft.com/en-us/sql/t-sql/statements/merge-transact-sql?view=sql-server-ver15
There is a simple solution! I Found it in this post!
INSERT INTO users (Username)
SELECT ('username')
WHERE NOT EXISTS(SELECT * FROM users WHERE Username= 'username')
In my project I needed to do it with 2 values. So my code was:
INSERT INTO MyTable (ColName1, ColName2)
SELECT 'Value1','Value2'
WHERE NOT EXISTS
(SELECT * FROM MyTable WHERE ColName1 = 'Value1' AND ColName2= 'Value2')
Hope this helps!
The following code is a method that returns 0 if user already exists and returns the new user ID that just added :
private int TryToAddUser(string UserName)
{
int res = 0;
try
{
string sQuery = " IF NOT EXISTS (select * from users where username = #username) \n\r" +
" BEGIN \n\r" +
" INSERT INTO users values (#username) \n\r" +
" SELECT SCOPE_IDENTITY() \n\r " +
" END \n\r " +
" ELSE SELECT 0";
using (System.Data.SqlClient.SqlCommand cmd = new System.Data.SqlClient.SqlCommand())
{
cmd.CommandText = sQuery;
cmd.Parameters.AddWithValue("#username",UserName);
cmd.Connection = new System.Data.SqlClient.SqlConnection("SomeSqlConnString");
cmd.Connection.Open();
res = (int)cmd.ExecuteScalar();
cmd.Connection.Close();
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
return res;
}
#gbn answer needs SQL server 2008 or higher. I tried a non-merge way to do it. Ugly perhaps, but it works.
declare #user varchar(50)
set #user = 'username'
insert into users (username)
select #user
where not exists (
select username
from users
where username = #user
);
If you want to test any of the answers, here is the SQL for the table -
CREATE TABLE [dbo].[users](
[userid] [int] NULL,
[username] [varchar](50) NULL
)
INSERT [dbo].[users] ([userid], [username]) VALUES (1, N'John')
INSERT [dbo].[users] ([userid], [username]) VALUES (2, N'David')
INSERT [dbo].[users] ([userid], [username]) VALUES (3, N'Stacy')
INSERT [dbo].[users] ([userid], [username]) VALUES (4, N'Arnold')
INSERT [dbo].[users] ([userid], [username]) VALUES (5, N'Karen')
Here is a (probably oversimplified) example that addresses your issue. Note that it is always best to use parameters to prevent injection attacks, especially when verifying users.
CREATE PROCEDURE AddUser
#username varchar(20)
AS
IF NOT EXISTS(SELECT username FROM users WHERE username=#username)
INSERT INTO users(username) VALUES (#username)

Select highest number from table when number stored as string?

I'm trying to write a windows forms app in C# .Net 4 it connects to a SQL Server 2008 database and I want to Select highest number from a table where the number is stored as string!
string SQL = "select MAX(CONVERT(int, myField)) from myTable where myCode = '" + theCust + "'";
I have also tried Max(CAST(myField as Int)) in the select statement but both fail to return anything even though the Database has for the theCust two rows with 10001 and 10002. The Error i Get is "Enumeration yielded no results"
What am I doing wrong?
I'm using the in built System.Data.SqlClient and if I just do a
string SQL = "select myField from myTable where myCode = '" + theCust + "'";
it returns both numbers as strings. I know I could sort them in code but if the Database gets large that would not be a good approach!
I just tried it again with an int Field in the db and still got the same error! Is Max the wrong thing to be using?
You can try it like this:
SELECT TOP 1 CAST(MyColumn AS int) AS TheMax
FROM MyTable
ORDER BY TheMax DESC
So (using the sloppy method, always paramaterize!)
String sql = "SELECT TOP 1 CAST(MyColumn AS int) AS TheMax FROM MyTable WHERE MyParam = '" + param + "' ORDER BY TheMax Desc";
//Fill DataAdapter/DataReader etc.
Have this function in your database(s):
CREATE FUNCTION dbo.IsAllDigits (#MyString VARCHAR(8000))
RETURNS TABLE AS
RETURN (
SELECT CASE
WHEN #MyString NOT LIKE '%[^0-9]%'
THEN 1
ELSE 0
END AS IsAllDigits
)
because it's better than the in-build ISNUMERIC() in T-SQL.
Then you can use this query to get set of strings that convert to integer types without errors, and filter them like with TOP 1.
SELECT TOP 1 MyColumn AS TheMax
FROM MyTable
WHERE IsAllDigits(MyColumn)=1
ORDER BY MyColumn DESC

Categories

Resources