SQL alter script - copying from one table to another - sql

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

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

Postgres: Create Column and Update Column Values with Query Output

I have a file that has no Primary Key. In order to load the file and perform analysis I want to concatenate 2 existing columns and send the output to a new column. I'm then going to do a hash of this resultant column and use that as a PK.
I haven't even got to the hash part as I can not for the life of me work out how to populate my concatenated column with data.
The query I'm trying to use is:
ALTER TABLE members_250815
ADD COLUMN email_id VARCHAR;
UPDATE members_250815
INSERT INTO members_250815(email_id)(
SELECT ARRAY_TO_STRING(ARRAY[emailaddress, id], ' ') AS email_id
FROM members_250815);
As seperate queries both
ALTER TABLE members_250815
ADD COLUMN email_id VARCHAR;
and
SELECT ARRAY_TO_STRING(ARRAY[emailaddress, id], ' ') AS email_id
FROM members_250815;
seem to work as I want them to (ie - 1) create the new column and 2) concatenate the 2 columns) however my issue seems to be in joining it all together.
Am I doing something really stupid? I have tried to research this for hours but I am getting nowhere. Essentially the task I am trying to achieve is:
Create new column on existing table
Concatenate 2 existing columns
Take the result of the concatenation and update this new column with this data without affecting any of my other existing data.
Is this possible?
Many thanks in advance
---Update 260815
Many thanks for the quick advice guys, much appreciated!
Using a combination of your advice I have gotten to here:
CREATE TABLE members_update AS
SELECT * FROM members_250815;
ALTER TABLE members_update
ADD COLUMN email_id VARCHAR;<br/>
UPDATE members_update
SET email_id = email || id;
ALTER TABLE members_update
ADD COLUMN hashed_primary_key VARCHAR;
UPDATE members_update
SET hashed_primary_key = md5(email_id::VARCHAR);
ALTER TABLE members_update
ADD CONSTRAINT hashed_primary_key_urn
PRIMARY KEY (hashed_primary_key);
ANALYSE members_update;
I have checked and everything works as expected up until adding the primary key. This is because it turned out that my email field contains numerous NULL values which are then carried into to the email_id and hashed columns and stop the hashed version from being used as the PK.
As such I have been experimenting with IF THEN ELSE and WHERE ELSE statements like
UPDATE members_update(
IF email IS NOT NULL
THEN SET email_id = email || id
ELSE SET email_id = id
END IF);
I have tried numerous combinations, with and without brackets etc and I can never get it to work! I think I am close but just can't seem to make this final part work - has anyone got any ideas?
Many thanks,
Mark
The problem is your update statement is wrong
You need SET, and CASE Should be:
ALTER TABLE members_250815
ADD COLUMN email_id VARCHAR;
UPDATE members_250815
SET email_id = CASE
WHEN email IS NULL THEN id
ELSE email || id
END;
ARRAY_TO_STRING(ARRAY[emailaddress, id], ' ') may also work, but a further research will be necesarry to know if is more eficient than just concatenate the string.
Better way to create an PK column:
Just alter the table and add a serial column
SQL Fiddle Demo
CREATE TABLE members_250815
("DMDUNIT" varchar(5),
"IND" int)
;
INSERT INTO members_250815 VALUES ('TM001', 1);
INSERT INTO members_250815 VALUES ('TM002', 1);
INSERT INTO members_250815 VALUES ('TM003', 1);
ALTER TABLE members_250815
ADD COLUMN id SERIAL NOT NULL PRIMARY KEY;
Aditional Info
In postgres updates are very slow. So in some cases is better consider just create a new table:
CREATE new_table AS
SELECT *, CASE
WHEN email IS NULL THEN id
ELSE email || id
END as email_id
FROM members_250815
and then
DROP TABLE IF EXITS members_250815;
ALTER TABLE new_table RENAME TO members_250815

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?

SQLite renumber ID using cycle

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;

Checking sql unique value with constraint

I have a situation where a table has three columns ID, Value and status. For a distinct ID there should be only one status with value 1 and it should be allowed for ID to have more then one status with value 0. Unique key would prevent ID of having more then one status (0 or 1).
Is there a way to solve this, maybe using constraints?
Thanks
You can create an indexed view that will uphold your constraint of keeping ID unique for [Status] = 1.
create view dbo.v_YourTable with schemabinding as
select ID
from dbo.YourTable
where [Status] = 1
go
create unique clustered index UX_v_UniTest_ID on v_YourTable(ID)
In SQL Server 2008 you could use a unique filtered index instead.
If the table can have duplicate ID values, then a check constraint wouldn't work for your situation. I think the only way would be to use a trigger. If you are looking for an example then I can post one. But in summary, use a trigger to test if the inserted/updated ID has a status of 1 that is duplicated across the same ID.
EDIT: You could always use a unique constraint on ID and Value. I'm thinking that will give you what you are looking for.
You could put this into an insert/ update trigger to check to make sure only one combination exists with the 1 value; if your condition is not met, you could throw a trappable error and force the operation to roll back.
If you can use NULL instead of 0 for a zero-status, then you can use a UNIQUE constraint on the pair and it should work. Since NULL is not an actual value (NULL != NULL), then rows with multiple nulls should not conflict.
IMHO, this basically is a normalisation problem. The column named "id" does not uniquely address a row, so it can never be a PK. At least a new (surrogate) key(element) is needed. The constraint itself cannot be expressed as an expression "within the row", so it has to be expressed in terms of a FK.
So it breaks down into two tables:
One with PK=id, and a FK REFERENCING two.sid
Two with PK= surrogate key, and FK id REFERENCING one.id
The original payload "value" also lives here.
The "one bit variable" disappears, because it can be expressed in terms of EXISTS. (effectively table one points to the row that holds the token)
[I expect the Postgres rule system could be used to use the above two-tables-model to emulate the intended behaviour of the OP. But that would be an ugly hack...]
EDIT/UPDATE:
Postgres supports partial/conditional indices. (don't know about ms-sql)
DROP TABLE tmp.one;
CREATE TABLE tmp.one
( sid INTEGER NOT NULL PRIMARY KEY -- surrogate key
, id INTEGER NOT NULL
, status INTEGER NOT NULL DEFAULT '0'
/* ... payload */
);
INSERT INTO tmp.one(sid,id,status) VALUES
(1,1,0) , (2,1,1) , (3,1,0)
, (4,2,0) , (5,2,0) , (6,2,1)
, (7,3,0) , (8,3,0) , (9,3,1)
;
CREATE UNIQUE INDEX only_one_non_zero ON tmp.one (id)
WHERE status > 0 -- "partial index"
;
\echo this should succeed
BEGIN ;
UPDATE tmp.one SET status = 0 WHERE sid=2;
UPDATE tmp.one SET status = 1 WHERE sid=1;
COMMIT;
\echo this should fail
BEGIN ;
UPDATE tmp.one SET status = 1 WHERE sid=4;
UPDATE tmp.one SET status = 0 WHERE sid=9;
COMMIT;
SELECT * FROM tmp.one ORDER BY sid;
I came up with a solution
First create a function
CREATE FUNCTION [dbo].[Check_Status] (#ID int)
RETURNS INT
AS
BEGIN
DECLARE #r INT;
SET #r =
(SELECT SUM(status) FROM dbo.table where ID= #ID);
RETURN #r;
END
Second create a constraint in table
([dbo].[Check_Status]([ID])<(2))
In this way one ID could have single status (1) and as many as possible status (0).
create function dbo.IsValueUnique
(
#proposedValue varchar(50)
,#currentId int
)
RETURNS bit
AS
/*
--EXAMPLE
print dbo.IsValueUnique() -- fail
print dbo.IsValueUnique(null) -- fail
print dbo.IsValueUnique(null,1) -- pass
print dbo.IsValueUnique('Friendly',1) -- pass
*/
BEGIN
DECLARE #count bit
set #count =
(
select count(1)
from dbo.MyTable
where #proposedValue is not null
and dbo.MyTable.MyPkColumn != #currentId
and dbo.MyTable.MyColumn = #proposedValue
)
RETURN case when #count = 0 then 1 else 0 end
END
GO
ALTER TABLE MyTable
WITH CHECK
add constraint CK_ColumnValueIsNullOrUnique
CHECK ( 1 = dbo.IsValueNullOrUnique([MyColumn],[MyPkColumn]) )
GO