SQLite renumber ID using cycle - sql

Hello I have table with many inserted row. I need to renumber all row by id and order them.
I have found this code but it does not work for me.
SET #i = 100;
UPDATE "main"."Categories" SET ID = (#i := #i +1) WHERE "Name" = "White";
ALTER TABLE "main"."Categories" AUTO_INCREMENT = 1
So using code above I expected renumbered all records that have name - white and start insert them from 100 with increment 1. But it is not work for me. Maybe there is some problem in my code but maybe it is a difference between SQL and SQLite query.
This how I created table:
CREATE TABLE Categories (id INTEGER PRIMARY KEY, Name TEXT, Free NUMERIC)
I hope there is already made solution how to do it because I don't want to do it manually :)

That code is not standard SQL.
SQLite does not have many programming constructs because it is designed to be an embedded database where it is more natural to have the logic in the host language.
If you want to do this in SQL, try the following:
First, create a temporary table so that we have an autoincrement column that can be used for counting:
CREATE TEMPORARY TABLE new_ids(i INTEGER PRIMARY KEY, old_id INTEGER);
Insert a dummy record to ensure that the next new record starts at 100, then insert all the IDs of the Categories table that you want to change:
INSERT INTO new_ids VALUES(99, NULL);
INSERT INTO new_ids SELECT NULL, id FROM "Categories" WHERE "Name" = 'White';
DELETE FROM new_ids WHERE i = 99;
Then we can change all these IDs in the original table:
UPDATE "Categories"
SET id = (SELECT i FROM new_ids WHERE old_id = "Categories".id)
WHERE id IN (SELECT old_id FROM new_ids);
DROP TABLE new_ids;

Related

Manually Checking of Value Changes in Tables for SQL

An example to the problem:
There are 3 columns present in my SQL database.
+-------------+------------------+-------------------+
| id(integer) | age(varchar(20)) | name(varchar(20)) |
+-------------+------------------+-------------------+
There are a 100 rows of different ids, ages and names. However, since many people update the database, age and name constantly change.
However, there are some boundaries to age and name:
Age has to be an integer and has to be greater than 0.
Name has to be alphabets and not numbers.
The problem is a script to check if the change of values is within the boundaries. For example, if age = -1 or Name = 1 , these values are out of the boundaries.
Right now, there is a script that does insert * into newtable where age < 0 and isnumeric(age) = 0 or isnumeric(name) = 0;
The compiled new table has rows of data that have values that are out of the boundary.
I was wondering if there is a more efficient method to do such checking in SQL. Also, i'm using microsoft sql server, so i was wondering if it is more efficient to use other languages such as C# or python to solve this issue.
You can apply check constraint. Replace 'myTable' with your table name. 'AgeCheck' and 'NameCheck' are names of the constraints. And AGE is the name of your AGE column.
ALTER TABLE myTable
ADD CONSTRAINT AgeCheck CHECK(AGE > 0 )
ALTER TABLE myTable
ADD CONSTRAINT NameCheck CHECK ([Name] NOT LIKE '%[^A-Z]%')
See more on Create Check Constraints
If you want to automatically insert the invalid data into a new table, you can create AFTER INSERT Trigger. I have given snippet for your reference. You can expand the same with additional logic for name check.
Generally, triggers are discouraged, as they make the transaction lengthier. If you want to avoid the trigger, you can have a sql agent job to do auditing on regular basis.
CREATE TRIGGER AfterINSERTTrigger on [Employee]
FOR INSERT
AS
BEGIN
DECLARE #Age TINYINT, #Id INT, Name VARCHAR(20);
SELECT #Id = ins.Id FROM INSERTED ins;
SELECT #Age = ins.Age FROM INSERTED ins;
SELECT #Name = ins.Name FROM INSERTED ins;
IF (#Age = 0)
BEGIN
INSERT INTO [EmployeeAudit](
[ID]
,[Name]
,[Age])
VALUES (#ID,
#Name,
#Age);
END
END
GO

SQL Server : setting the value to the next highest id number when the table is not set to auto increment

I am using SQL Server 2008 and some times SQL Server 2012 and I need to do an insert on a pre-existing database where the schema does not seem to have the id column set for auto-increment.
So for example, here is a basic table My_Table that I have:
[Id] [Name]
-------------
1 JOHN
2 BOB
3 SALLY
I need to do an insert to get the MAX value of the last Id and then use this value for my new insert statement. I believe this is the best approach.
So for example the "pseudo syntax" for the insert would be:
INSERT INTO My_Table
VALUES(MAX(Id) + 1, 'MIKE');
How is the best way to do this when the Id is not set in the table schema to auto increment?
Note: I am using Microsoft SQL Server.
Thanks for any advice.
I recommend changing the structure of the table to use an identity column. The table definition should be:
create table my_table (
id int identity(1, 1) primary key,
. . .
);
This is the right solution. If you can't do that, you can express the logic as:
insert to my_table (id, name)
select coalesce(1 + max(id), 0), 'Mike'
from my_table;
However, this suffers from race conditions. Two threads could attempt an insert at the same time and end up with the same id. Avoiding such race conditions is why you want the database to do the work.
If you are in control of all inserts into the table, you can use a sequence as well.
You could create another table with an IDENTITY column:
CREATE TABLE ID_Insert (
ID INT IDENTITY(234, 1) primary key,
Val smallint null
)
(Where your seed value will be MAX(ID) + 1)
Insert any value into this table:
Insert ID_Insert(Val) values(NULL)
Use SCOPE_IDENTITY() to get the ID of the inserted value and use that in your insert into your other table.
NOTE: I have not tested this, but it gets around all the issues raised so far, so any criticism is welcome.
If you can't change the table structure so that the Id is an IDENTITY column, then this is probably your best bet:
SET XACT_ABORT ON;
BEGIN TRANSACTION;
DECLARE #maxId int = (SELECT MAX(id) FROM my_table);
INSERT INTO my_table (id, name)
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 1)) + #maxId, name
FROM my_other_table;
COMMIT TRANSACTION;
This works for batch inserts, not only single name inserts.

SQL alter script - copying from one table to another

I'm trying to figure out how to implement the alter script described below. I'm familiar with the basics if insert/select already, but this is a lot more complex.
I have a legacy table and need to move its data to a new table with more columns. The new table has already been made public to some select users, who may have already manually moved the common data over.
So for each row in LegacyTable:
see if it already exists in NewImprovedTable (by checking for a match on a string field that exists in both tables)
if not, copy its over to NewImprovedTable
regardless of whether it had been copied to NewImprovedTable automatically just now, or previously by the user...
auto-populate a new Name field in NewImprovedTable (must be unique - e.g. "Legacy1", "Legacy2", etc.)
set an IsLegacy flag in NewImprovedTable
I need to implement this in both MS SQL and Oracle, but once I work out the logic on one I can figure out the syntax on the other.
The solution I settled on (in SQL Server - still need to port to Oracle):
IF NOT EXISTS(SELECT 1
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'NewImprovedTable'
AND COLUMN_NAME = 'legacyFlg')
BEGIN
ALTER TABLE [NewImprovedTable]
ADD legacyFlg TINYINT NULL
ALTER TABLE [LegacyTable]
ADD improvedId INT NULL
END
GO
IF NOT EXISTS(SELECT 1 FROM ImprovedTable WHERE legacyFlg = 1)
BEGIN
MERGE ImprovedTable AS TARGET
USING LegacyTable AS SOURCE
ON (TARGET.stringField = SOURCE.stringField)
WHEN NOT MATCHED THEN
INSERT (name, <other columns>, legacyFlg)
VALUES('Legacy' + SOURCE.stringField, <other column values>, 1)
WHEN MATCHED THEN
UPDATE SET TARGET.legacyFlg = 1;
END
GO
IF NOT EXISTS(SELECT 1 FROM LegacyTable WHERE improvedId <> 0)
BEGIN
MERGE LegacyTable AS TARGET
USING NewImprovedTable AS SOURCE
ON (SOURCE.stringField = TARGET.stringField)
WHEN MATCHED THEN
UPDATE SET TARGET.improvedId = SOURCE.pId;
END
GO
You could try using this, where 'input' is the string you are trying to confirm if already exists:
SELECT * FROM`NewImprovedTable` WHERE `Variable`='input'
This will return the whole row if found any match, if not it will return null, you can play with that
As for the unique ID field you need to create a primary key on your table with the auto increment option enable, for example
CREATE TABLE Persons
(
P_Id INT NOT NULL AUTO_INCREMENT,
LastName varchar(255) NOT NULL,
FirstName varchar(255),
Address varchar(255),
City varchar(255),
PRIMARY KEY (P_Id)
)
In this last example P_Id is set as an autoincrement variable, each time you crate a new row it will auto fill this column with a unique number.
You should check this page
http://www.w3schools.com/sql/sql_primarykey.asp

3 tables, 2 DBs, 1 Stored Procedure

I'm a novice when it comes to Stored Procedures in SQL Server Management Studio. I have an application that I was told to make the following changes to using a stored procedure:
Step 1. User types in an item number.
Step 2. Customer name, address, etc. displays in the other fields on the same form.
There are 3 tables: Bulk orders, Small orders, and Customer information.
Bulk orders and small orders are in Database_1 and Customer information is in Database_2.
The primary key for small orders is the order number. A column in small orders contains the customer number for each order. That customer number is the primary key in the customer table.
The bulk orders table is similar.
I want to include a conditional statement that says: if order number is found in small orders table, show data from customer table that coorelates with that order number. I've attempted this multiple ways, but keep getting a "The multi-part identifier.... could not be bound" error.
I.E:
SELECT DB1.db.Customer_Table.Customer_Column AS CustomerNumber;
IF(CustomerNumber NOT LIKE '%[a-z]%')
BEGIN
SELECT * FROM db.small_orders_table;
END
ELSE
BEGIN
SELECT * FROM db.buld_orders_table;
END
Please help.
Sounds like it's 2 databases on the same server...in that case, you'll need to specify the fully qualified table name (database.schema.table) when referencing a table on the other database from where your stored procedure is found.
Database_1.db.small_orders_tables
first of all, you cannot use aliases as variables. If you want to assign a value to a variable in order to test it, you have to do a SELECT statement like SELECT #var = DB1.db.Customer_Table.Customer_Column FROM <YourTableFullName> WHERE <condition>. Then you can use the #var (which must be declared before) for your test.
About the error you're experiencing, youre using fully qualified names in a wrong way. If you're on the same server (different databases), you need to specify just the database name on the top and then the schema of your objects. Suppose to have the following database objects on the Database1:
USE Database1;
GO
CREATE TABLE dbo.Table1
(
id int IDENTITY(1, 1) NOT NULL PRIMARY KEY CLUSTERED
, val varchar(30)
);
GO
INSERT INTO dbo.Table1 (val) VALUES ('test1');
GO
INSERT INTO dbo.Table1 (val) VALUES ('test2');
GO
INSERT INTO dbo.Table1 (val) VALUES ('test3');
GO
And the following ones on Database2:
USE Database2;
GO
CREATE TABLE dbo.Table2
(
id int IDENTITY(1, 1) NOT NULL PRIMARY KEY CLUSTERED
, val varchar(30)
);
GO
Now, suppose that you want to read from the first table the value with id = 2, and then to apply your IF. Let's declare a variable and test it:
USE Database1;
GO
DECLARE #var varchar(30);
-- since you're on Database1, you don't need to specify full name
SELECT #var = val FROM dbo.Table1 WHERE id = 2;
IF #var = 'test2'
BEGIN
SELECT id, val FROM dbo.Table1;
END
ELSE
BEGIN
-- in this case the database name is needed
SELECT id, val FROM Database2.dbo.Table2;
END
GO
Does it help?

Generating the Next Id when Id is non-AutoNumber

I have a table called Employee. The EmpId column serves as the primary key. In my scenario, I cannot make it AutoNumber.
What would be the best way of generating the the next EmpId for the new row that I want to insert in the table?
I am using SQL Server 2008 with C#.
Here is the code that i am currently getting, but to enter Id's in key value pair tables or link tables (m*n relations)
Create PROCEDURE [dbo].[mSP_GetNEXTID]
#NEXTID int out,
#TABLENAME varchar(100),
#UPDATE CHAR(1) = NULL
AS
BEGIN
DECLARE #QUERY VARCHAR(500)
BEGIN
IF EXISTS (SELECT LASTID FROM LASTIDS WHERE TABLENAME = #TABLENAME and active=1)
BEGIN
SELECT #NEXTID = LASTID FROM LASTIDS WHERE TABLENAME = #TABLENAME and active=1
IF(#UPDATE IS NULL OR #UPDATE = '')
BEGIN
UPDATE LASTIDS
SET LASTID = LASTID + 1
WHERE TABLENAME = #TABLENAME
and active=1
END
END
ELSE
BEGIN
SET #NEXTID = 1
INSERT INTO LASTIDS(LASTID,TABLENAME, ACTIVE)
VALUES(#NEXTID+1,#TABLENAME, 1)
END
END
END
Using MAX(id) + 1 is a bad idea both performance and concurrency wise.
Instead you should resort to sequences which were design specifically for this kind of problem.
CREATE SEQUENCE EmpIdSeq AS bigint
START WITH 1
INCREMENT BY 1;
And to generate the next id use:
SELECT NEXT VALUE FOR EmpIdSeq;
You can use the generated value in a insert statement:
INSERT Emp (EmpId, X, Y)
VALUES (NEXT VALUE FOR EmpIdSeq, 'x', 'y');
And even use it as default for your column:
CREATE TABLE Emp
(
EmpId bigint PRIMARY KEY CLUSTERED
DEFAULT (NEXT VALUE FOR EmpIdSeq),
X nvarchar(255) NULL,
Y nvarchar(255) NULL
);
Update: The above solution is only applicable to SQL Server 2012+. For older versions you can simulate the sequence behavior using dummy tables with identity fields:
CREATE TABLE EmpIdSeq (
SeqID bigint IDENTITY PRIMARY KEY CLUSTERED
);
And procedures that emulates NEXT VALUE:
CREATE PROCEDURE GetNewSeqVal_Emp
#NewSeqVal bigint OUTPUT
AS
BEGIN
SET NOCOUNT ON
INSERT EmpIdSeq DEFAULT VALUES
SET #NewSeqVal = scope_identity()
DELETE FROM EmpIdSeq WITH (READPAST)
END;
Usage exemple:
DECLARE #NewSeqVal bigint
EXEC GetNewSeqVal_Emp #NewSeqVal OUTPUT
The performance overhead of deleting the last inserted element will be minimal; still, as pointed out by the original author, you can optionally remove the delete statement and schedule a maintenance job to delete the table contents off-hour (trading space for performance).
Adapted from SQL Server Customer Advisory Team Blog.
Working SQL Fiddle
The above
select max(empid) + 1 from employee
is the way to get the next number, but if there are multiple user inserting into the database, then context switching might cause two users to get the same value for empid and then add 1 to each and then end up with repeat ids. If you do have multiple users, you may have to lock the table while inserting. This is not the best practice and that is why the auto increment exists for database tables.
I hope this works for you. Considering that your ID field is an integer
INSERT INTO Table WITH (TABLOCK)
(SELECT CASE WHEN MAX(ID) IS NULL
THEN 1 ELSE MAX(ID)+1 END FROM Table), VALUE_1, VALUE_2....
Try following query
INSERT INTO Table VALUES
((SELECT isnull(MAX(ID),0)+1 FROM Table), VALUE_1, VALUE_2....)
you have to check isnull in on max values otherwise it will return null in final result when table contain no rows .