SQL Server 2008 R2 - DROP COLUMN, INFORMATION_SCHEMA.COLUMNS and IF EXISTS - sql-server-2008

I have a column that is functionally a duplicate of another column. I want to copy the value of the surplus column to the other and then drop the extraneous column.
The problem is the script is conditional amid other changes to the database - I like to be able to restore the database to the exact state it was in via an "undo" script. This script and its corresponding "do" script are written conditionally so that they can be run repeatedly without error.
This particular block when executed a second time, fails with invalid column. It seems to think the column is still there even though both INFORMATION_SCHEMA.COLUMNS and sys.columns report no column exists.
if exists (select * from sys.objects where name = 'flint')
drop table flint
create table flint ( fred int, barny int )
go
select 1 from INFORMATION_SCHEMA.COLUMNS where COLUMN_NAME = 'barny' and TABLE_NAME = 'flint'
if exists (select 1 from INFORMATION_SCHEMA.COLUMNS where COLUMN_NAME = 'barny' and TABLE_NAME = 'flint')
begin
update flint set fred = barny
alter table flint drop column barny
end
go
select 1 from INFORMATION_SCHEMA.COLUMNS where COLUMN_NAME = 'barny' and TABLE_NAME = 'flint'
if exists (select 1 from INFORMATION_SCHEMA.COLUMNS where COLUMN_NAME = 'barny' and TABLE_NAME = 'flint')
begin
update flint set fred = barny
alter table flint drop column barny
end
go
Why does the second block get executed and fail with 'invalid column barny'?

Since your code is split into batches by the GO delimiters, the second block is only submitted after the column has been dropped by the first block. The second block does not get executed but it does get compiled and bound to database objects. Hence name resolution fails and you get the error message. The message is coming from the parser, not the database engine.
Bizarrely, if you remove all the GOs it will fail at the second SELECT 1.. with the same error.

It's to do with SQL Server batching but damned if I can figure it out why at the moment. Remove the GO from between the blocks and it works as expected. My guess is that the column is dropped during the second batch but the third batch has already been compiled ready to send so it only fails when it tries to execute on the server. I'm going to do some reading on this because it's a neat little gotcha.

D'oh! The parser is looking at the references to the removed column in the block before we know the result of EXISTS. So if I use dynamic SQL, all is well.
if exists (select 1 from INFORMATION_SCHEMA.COLUMNS where COLUMN_NAME = 'barny' and TABLE_NAME = 'flint')
begin
declare #q nchar(100)
select #q='update flint set fred = barny;
alter table flint drop column barny'
exec sp_executesql #q
end
go
Thanks Michael and Steve!

Related

How to use a variable with dynamic field names for ON DUPLICATE KEY in a trigger (MariaDB/MySQL)

I have hundreds of databases with some structurally identical (but over time changing) tables. Data of a certain table (from all DBS) should be copied into one central table ('ex_objects') in a central database ('db_central'; there are no pk conflicts). I've used a trigger in each DB for this purpose. But since the table structure is changing almost on a daily basis, it's a pain to update the fields in the ON DUPLICATE KEY part of the trigger's query. And someone could forget to modify the trigger after modifying the table structure. So I came across a possible solution to build that particular part of the query dynamically. This actually works on a script (PHP) basis, but I don't get the trigger done. I don't see what I am missing here.
BEGIN
DECLARE VAL_FIELDS TEXT;
SET VAL_FIELDS = (SELECT GROUP_CONCAT( CONCAT(COLUMN_NAME,"=values(", COLUMN_NAME,")") SEPARATOR ", ") FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = 'db_central' AND TABLE_NAME = 'ex_objects');
-- SELECT GROUP_CONCAT( CONCAT(COLUMN_NAME,"=values(", COLUMN_NAME,")") SEPARATOR ", ") INTO VAL_FIELDS FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = 'db_central' AND TABLE_NAME = 'ex_objects';
-- SELECT #VAL_FIELDS := GROUP_CONCAT( CONCAT(COLUMN_NAME,"=values(", COLUMN_NAME,")") SEPARATOR ", ") INTO VAL_FIELDS FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = 'db_central' AND TABLE_NAME = 'ex_objects';
IF NEW.online = 1 THEN
INSERT INTO db_central.ex_objects
SELECT * FROM ex_objects WHERE id = NEW.id AND client_id = NEW.client_id AND NEW.online = 1
ON DUPLICATE KEY UPDATE VAL_FIELDS;
END IF;
END
I get the error that there's something wrong at ; END IF; END. Well, that means for me, that either the VAL_FIELDS variable after KEY UPDATE isn't recognized at all or the parser expects at least one equation (something like VAL_FIELDS = whatever). But in this case, it wouldn't solve my underlying problem at all.
The SELECT GROUP_CONCAT( CONCAT(COLUMN_NAME,"=v ... FROM INFORMATION_SCHEMA.C ... Query works well and results in something similar to id=values(id), xfield=values(xfield), yfield=values(yfield) (but with a few hundred fields, since the table is actually pretty huge).
The full error: SQL Error (1064): You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ';
END IF;
END' at line 10
You must build the SQL using CONCAT, etc, then prepare and execute it. There is no 'interpolation'.
Since it is a TRIGGER, that won't work.
However, since a TRIGGER applies to a particular table, you may as well simply spell out the query, not construct it. You have most of what it takes to manually get the query generated for you (SELECT ... I_S ...). Add some more columns for OLD.col to fill out the thing; viola, you have the query that you need.

Hidden character in SQL column

I've copied and pasted an SQL statement which simply adds a column into the table:
ALTER TABLE `users` ADD COLUMN `favourites​` TEXT;
However, where I have copied and pasted, the favourites name has some how managed to pick up a hidden character.
I have left the hidden character in the example above for you to see/or not see as it may be!
It's favourites?, with what appears to be a question mark.
THE PROBLEM: I need to delete this column and re-add it manually so that the hidden character is not present. The problem is that any SQL statement I do, it doesn't recognise the the column name favourites because of the hidden character and I don't know how to target it.
Has anyone got any idea how to get around this?
Do the same use show
SHOW COLUMNS FROM your_table;
for obtain the column name and then copy the column you need in your delete command
alter table your_table drop column your_column_copied
and the add the column with the right name
alter table your_table add column your_column
otherwise, if is impossible get the column_name, you can create a temp table without the wrong column with create/select command
create table (col1, col2, col3)
select col1,col2, col3
from you_table
then drop the original table and rename the temporary table and last add your column with right name
You could use dynamic query:
DECLARE #sql nvarchar(800)
SELECT #sql = 'ALTER TABLE users DROP COLUMN ' + COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS where TABLE_NAME = 'users' and COLUMN_NAME LIKE '%favour%'
EXEC sp_executesql #sql
You can obtain the column name by querying INFORMATION_SCHEMA and prepare statement with the obtained column name. Something like this:
DECLARE #StrangeColumnName NVARCHAR(16) := ''
SELECT #StrangeColumnName := COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'users' AND COLUMN_NAME LIKE 'favourites%'
DECLARE #SqlText NVARCHAR(32) := 'ALTER TABLE status DROP COLUMN ?'
EXECUTE #SqlText USING #StrangeColumnName
Maybe open the information schema of the table and copy the column name from there? i don't know which Database are you using. Please update for more information.
If you have access to phpMyAdmin or, if you can create a small script to run this script:
SELECT COLUMN_NAME, FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_name = 'your_tbl_name'
OR
SHOW COLUMNS
FROM 'your_tbl_name
and copy the column name from the page.
next, you can drop that column using
alter table your_tbl_name drop column column_name;
and you already know how to add a column in mysql so, i guess that should solve your problem.
I hope you do know that you can not comment if your reputation is below 50 and if you didn't provide enough information, those who might actually have an answer for you, but have below 50 rep, will have to post it in answers. or would you like to eliminate those who are 50 rep as candidates for helping you?
In order to delete a column you can use:
alter table <tblname> drop column <colname>
and then after deleting the column you can add the column by writing below code:
ALTER TABLE users ADD COLUMN favourites​ TEXT;
Some possibilities:
Using phpmyadmin
Using a tool to talk directly to the database like navicat etc

MYSQL Modify Column Data Type Script with Checking of Current Column Data Type

I have here a mysql script which alters my 'Job' table and changes the DESCRIPTION column data type to TEXT. However, I have this script together with all the other scripts which are sometimes run multiple times.
My question is, what do I need to add to my script so that it would check if the data type of DESCRIPTION column is already TEXT or not? This script takes too long to execute due to huge data and I don't want it to be executed again if the DESCRIPTION column has already been changed to TEXT.
ALTER TABLE Job
MODIFY DESCRIPTION TEXT;
SELECT
COLUMN_NAME, DATA_TYPE
FROM
INFORMATION_SCHEMA.COLUMNS
WHERE
TABLE_SCHEMA = 'YOUR_DB_NAME'
AND
TABLE_NAME = 'YOUR_TABLE_NAME'
AND
COLUMN_NAME = 'YOUR_COLUMN_NAME';
This will give you datatype of asked column.
Using If condition you can run your alter table command;
Here's what I did. It's a long way but it worked. It needs to be in a stored procedure. Thanks for your help #shahmanthan9. If you guys know a better way please post it here. Thanks!
DROP PROCEDURE IF EXISTS sp_JobUpdateDescriptionColumnType;
CREATE PROCEDURE sp_JobUpdateDescriptionColumnType()
DETERMINISTIC
SQL SECURITY DEFINER
COMMENT ''
BEGIN
IF NOT EXISTS( SELECT NULL
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'Job'
AND COLUMN_NAME = 'Description'
AND DATA_TYPE = 'text' )
THEN
ALTER TABLE Job
MODIFY Description TEXT;
END IF;
END;
CALL sp_JobUpdateDescriptionColumnType;
DROP PROCEDURE IF EXISTS sp_JobUpdateDescriptionColumnType;

Delete row if table exists SQL

I have a script that drops a load of tables using DROP TABLE IF EXISTS, this works.
There is also a delete in this script to DELETE a row from another table that I do not manage. This table may or may not exist.Is there any to check the table exists before attempting to delete a row?
this needs to work for MYSQL and SQLServer
thanks
Alex
To check in SQL SERVER,
IF (EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'TheSchema' AND TABLE_NAME = 'TheTable'))
BEGIN
--Do Stuff
END
To check in mysql:
You simply count:
SELECT COUNT(*)
FROM information_schema.tables
WHERE table_schema = '[database name]'
AND table_name = '[table name]';
This one deletes the row and does not complain if it can't.
DELETE IGNORE FROM table WHERE id=1
source here.
For SQL Server: You could use:
IF OBJECT_ID('tablename','U') IS NOT NULL
I dont think you'll find a common syntax between SQL server and my SQL. I mean, you can check if the table exsits on SQL Server using something like:
if exists(select * from sys.objects where name like 'table_name')
but mySql would have its own catalog.
Unless you write a script like:
if (sql_server) then
if exists(select * from sys.objects where name like 'table_name')
else --mySQl
--execute the mysql script
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[TABLE_NAME]') AND type in (N'U'))
It seems to me right the first item in the "Related" column on the right side answers your question.... Check if table exists in SQL Server
For MySQL
show tables like "test1";
For SQL Server
SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'testSchema' AND TABLE_NAME = 'test1'
A question you want to ask yourself (in terms of database design): Why are you trying to delete rows from a table you are not sure exists? If it doesn't, but you expect it does, wouldn't you rather create the table than not delete it?
Anyway, Chris Gesslers answer does exactly what you are asking in SQL Server, but there is some smell here.
The construct in MySQL you can use is
SELECT table_name
FROM information_schema.tables
WHERE table_schema = 'databasename'
AND table_name = 'tablename'
and check for results
you can use bellow code:
DECLARE #TABLENAME VARCHAR(20)='TableName';
IF (OBJECT_ID(#TABLENAME) IS NOT NULL )
BEGIN
execute(N'TRUNCATE TABLE ' + #TABLENAME + '' );
END
ELSE
BEGIN
PRINT 'Table NOT Exists'
END

Set all the columns of a mysql table to a particular value

hIs there any way to update all the columns of a mysql table for a particular record in one go to a particular value.
For e.g. I have a table that has around 70 columns , and they are by default set to 0 at the time of creating the table,when I add a new record via PHPmyadmin by just filling in one or two values and submitting it all the other fields are set to 0 , but I want to set all the fields to 1
many times ,so I need to set all the columns to 1 individually via PHPmyadmin
To speed-en up the process and
I tried
UPDATE tablename SET * = '1' WHERE id = '2' , but it does not work.
If anyone can provide a solution on similar lines , it would be great.
EDIT:
Is there a way without specifying all the 70 columns in the SQL statement? that what I am looking for. I do know how to update normally specifying columns in the SQL statement. Thank you.
If you are looking for a way to update all 70 columns to a single value with a short, simple statement, then I recommend that you write a stored procedure to do the update. That way you only need to write out the full update syntax once, and can re-use it over and over by calling the stored procedure.
CREATE PROCEDURE update_all_columns (p_new_value SMALLINT, p_id INT) ...
CALL update_all_columns(1,2);
Another trick is to use the information_schema.columns table to generate the update statement, making it less tedious to code the stored procedure.
Something like this:
SELECT concat('UPDATE ',
table_name,
' SET ',
group_concat(column_name separator ' = p_new_value, '),
' = p_new_value',
' WHERE id = p_id;') as sql_stmt
FROM information_schema.columns
WHERE table_schema = 'your_schema'
AND table_name = 'tablename'
AND column_name != 'id'
You have to name each column in an update statement.