Delete row in MySQL if any column has NULL value? - mysql

Is there a way to delete a row if any of the columns have a NULL value in them? I know I could do it one by one and check the columns but I would like to do this programmatically in MySQL where it would scale if I had 4 columns or 4000 columns. I believe I could do this with PHP, but I much rather do this in straight MySQL.
Thank you

Ok, since you just mentioned you are new to MySQL, your database design is new too and most probably does not have a lot of data as of now.
Why not kill the roots of the problem instead of letting those grow into a big tree and then looking for tools to cut all the branches first?
You should go ahead and use MySQL NOT NULL option and disallow null values for your column since you are deleting them. So if you don't need to keep any null values then you can simply disallow them and they will not be saved in the first place.
Queries come long after a proper database design, if your design does not match what your system requires then you can only optimize the queries to an extent. Base structure is the first thing you should learn and improve. Google and SO both are filled with thousands of articles on Efficient database design and some basic concepts to get started.

You could delete those records with without so much ORs:
DELETE FROM myTable
WHERE CONCAT(column1,column2,column3) is null
It may not make sense to delete what can be done, but can use this trick to get what should be done.
INSERT INTO NEW_TABLE
SELECT column1,column2,column3
FROM myTable
WHERE not CONCAT(column1,column2,column3) is null

I am not quite sure if this works in mysql because i can't test it (i edited the sql server code to may work with mysql)
CREATE PROCEDURE myProc()
BEGIN
DECLARE COL varchar(4000);
SELECT GROUP_CONCAT(C.COLUMN_NAME, ' IS NULL ' SEPARATOR ' OR ') FROM INFORMATION_SCHEMA.COLUMNS C WHERE C.TABLE_NAME = 'tbl_a' INTO COL;
SET #s = CONCAT('SELECT * FROM test WHERE ', COL);
PREPARE stmt FROM #s;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END//
i am using the select statement to check you just need to change that to delete

Related

MySql "view", "prepared statement" and "Prepared statement needs to be re-prepared"

I've a problem with MySql, here is the details :
I created a new schema/database, executed (only) this queries :
create table mytable (
id varchar(50) not null,
name varchar(50) null default '',
primary key (id));
create view myview as
select id,name from mytable;
insert into mytable values ('1','aaa');
insert into mytable values ('2','bbb');
insert into mytable values ('3','ccc');
and then, if I run these queries :
select * from mytable;
select * from myview;
prepare cmd from 'select id,name from mytable where id=?';
set #param1 = '2';
execute cmd using #param1;
the queries give the correct result (3 rows,3 rows,1 row).
but, the problem exists if I run this query:
prepare cmd from 'select id,name from myview where id=?';
set #param1 = '2';
execute cmd using #param1;
ERROR: #1615 - Prepared statement needs to be re-prepared
I've done some research and found that the increment of configurations below "may" solve the problem :
increase table_open_cache_instances value
increase table_open_cache value
increase table_definition_cache value
As far as I know, the queries above are the common and standard MySql queries, so I think there is no problem with the syntax.
I'm on a shared webhosting and using MySql version is 5.6.22
But the things that make me confused is, it only contain 1 schema/database, with 1 table with 3 short records and 1 view,
and I executed a common and standard MySql select query,
does the increment of values above really needed?
is there anyone with the same problem had increase the values and really solve the problem?
or, perhaps do you have any other solution which you think may or will works to solve this problem?
ps: it does not happen once or twice in a day (which assumed caused by some backup or related), but in all day (24 hours).
Thank you.
Do you do this after each execute?
deallocate prepare cmd;
The closest guess until now is some other shared members on the server dont write their code quite well (because it is a shared webhosting), either doing large alter while doing the large select, or dont deallocate the prepared statement after using it, like Rick James said. (want to make the post usefull, but I dont have the reputation, sorry Rick)
I can not make sure if the increment of "table_definition_cache" will works because the system administrator still wont change the value until now, but incase you having the same problem and you can modify it, it worth to try.
My current solution is I change all my views in my query strings into non-view or subqueries, it works for me, but the problem is still in the air.
eg. from
select myview.id, myview.name
from myview
inner join other_table on ...
where myview.id=?
into
select x.id, x.name
from (select id,name from mytable) x
inner join other_table on ...
where x.id=?

SQL: convert all columns to VarChar(255)

After some searching here on stackoverflow and on the web, I couldn't find the answer to my question. I'm not a real SQL talent, but I'm trying to covert all the columns in my table to varchar (255). It has about 600 columns which are all varchar, but the size limit varies. I would like them all to be 255. Is there a way to not having to do this manually? I work with MySQL.
Thanks!
You need to generate the alter table statement by pulling the data from the database.
select 'alter table MyTableName modify column ' + column_name + ' varchar(255);'
from information_schema where table_name = 'MyTableName'
And then paste the results of this command into the query window and run it -- making sure it does what you want it to do. Do a backup first.
Or you could make one big alter statement (if MySql wouldn't choke on it) by replacing the semicolon with a comma.
This isn't what you really need to do. You have something more important to do: NORMALIZE YOUR DATABASE
Now, It's impossible that you have a normalized table with 600 columns. Split your entities in that table correctly, following at least the 3rd normal form rules. After that, you'll have a much better database which is easier to mantain.
To do this, you'll need to drop your current table, therefore, you don't need to change all the types to varchar(255) because you'll fix them during the creation of other tables.
This would be a good start to read: http://en.wikipedia.org/wiki/Database_normalization (thanks to #Tim Schmelter from question's comments)
First of all as mentioned by others you better off normalize you data.
In the meantime you can achieve your goal with dynamic SQL like this
DELIMITER $$
CREATE PROCEDURE change_to_varchar255(IN _tname VARCHAR(64))
BEGIN
SET #sql = NULL;
SELECT GROUP_CONCAT(
CONCAT_WS(' ', 'CHANGE', COLUMN_NAME, COLUMN_NAME, 'VARCHAR(255)'))
INTO #sql
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = _tname
AND DATA_TYPE = 'varchar'
AND CHARACTER_MAXIMUM_LENGTH < 255
AND TABLE_SCHEMA = SCHEMA();
SET #sql = CONCAT_WS(' ', 'ALTER TABLE', _tname, #sql);
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END$$
DELIMITER ;
Sample usage:
CALL change_to_varchar255('table1');
Here is SQLFiddle demo
If you are using PhpMyAdmin or other, you can also click on the button to modify the table.
When you are on the web page, press Ctrl+Shift+J under Windows or Cmd+Opt+J under Mac to open the console window in the Chrome Developer tools. Now enter the following command to replace all occurrences of the number 255 with 100 :
document.body.innerHTML = document.body.innerHTML.replace(/255/g, "100").
Finally, click on the button to execute the query.

MySQL Trigger which compares all OLD and NEW for changes need to be simplified

I want my "audit" triggers to be simpler to maintain.
Actually, on a Before update trigger, the logic is as follows :
IF NEW.field <> OLD.field THEN INSERT INTO table SET field=OLD.field
I am checking every column for changes because I don't want to store rows which are not really updated. There are many monitored columns and when I change a table (adding or deleting a column), I have to edit the triggers to reflect the new table structure.
Is there a simple way of going through each colum like this :
FOR EACH COL BEGIN IF NEW.COL <> OLD.COL THEN SET changedetected=true; END IF; END
And after that, using a FOR loop the same way to populate the Audit table with all the table's columns ?
I don't know if I am being clear enough, please post comments if needed.
Many thanks for your help !
You can get the list of columns from
SELECT column_name
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_name = 'tbl_name'
ORDER BY ordinal_position;
then you can just wrap the query into a CURSOR and loop through the names.
For comparison itself, I suppose you could PREPARE a statement for it
SET #s = CONCAT('SELECT OLD.', col_name, ' = NEW.', col_name, ' INTO #cmp');
PREPARE stmt FROM #s;
EXECUTE stmt;
IF #cmp ...
You can't use prepared statements in MySQL triggers.

How do I change the Auto Increment counter in MySQL?

I have an ID field that is my primary key and is just an int field.
I have less than 300 rows but now every time someone signs up that ID auto inc is inputted really high like 11800089, 11800090, etc.... Is there a way to get that to come back down so it can follow the order (310,311,312).
Thanks!
ALTER TABLE table_name AUTO_INCREMENT=310;
Beware though, you don't want to repeat an ID. If the numbers are that high, they got that way somehow. Be very sure you don't have associated data with the lower ID numbers.
https://dev.mysql.com/doc/refman/8.0/en/example-auto-increment.html
There may be a quicker way, but this is how I would do it to be sure I am recreating the IDs;
If you are using MySQL or some other SQL server, you will need to:
Backup your database
Drop the id column
Export the data
TRUNCATE or 'Empty' the table
Recreate the id column as auto_increment
Reimport the data
This will destroy the IDs of the existing rows, so if these are important, it is not a viable option.
The auto increment counter for a table can be (re)set two ways:
By executing a query, like others already explained:
ALTER TABLE <table_name> AUTO_INCREMENT=<table_id>;
Using Workbench or other visual database design tool. I am gonna show in Workbench how it is done - but it shouldn't be much different in other tool as well. By right click over the desired table and choosing Alter table from the context menu. On the bottom you can see all the available options for altering a table. Choose Options and you will get this form:
Then just set the desired value in the field Auto increment as shown in the image.
This will basically execute the query shown in the first option.
Guessing that you are using mysql because you are using PHP. You can reset the auto_increment with a statement like
alter table mytable autoincrement=301;
Be careful though - because things will break when the auto inc value overlaps
I believe that mysql does a select max on the id and puts the next. Try updating the ids of your table to the desired sequence. The problem you will have is if they're linked you should put a Cascade on the update on the fk.
A query that comes to my mind is:
UPDATE Table SET id=(SELECT max(id)+1 FROM TAble WHERE id<700)
700 something less than the 11800090 you have and near to the 300 WHERE id>0;
I believe that mysql complaints if you don't put a where
I was playing around on a similar problem and found this solution:
SET #newID=0;
UPDATE `test` SET ID=(#newID:=#newID+1) ORDER BY ID;
SET #c = (SELECT COUNT(ID) FROM `test`);
SET #s = CONCAT("ALTER TABLE `test` AUTO_INCREMENT = ",#c);
PREPARE stmt FROM #s;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
I hope that helps someone in a similar situation!

How can I "select *" from a table in MySQL but omit certain columns?

I have a table with the following columns:
id,name,age,surname,lastname,catgory,active
Instead of: SELECT name,age,surname,lastname,catgory FROM table
How can I make something like this: SELECT * FROM table [but not select id,active]
While many say it is best practice to explicitly list every column you want returned, there are situations where you might want to save time and omit certain columns from the results (e.g. testing). Below I have given two options that solve this problem.
1. Create a Function that retrieves all of the desired column names: ( I created a schema called functions to hold this function)
DELIMITER $$
CREATE DEFINER=`root`#`%` FUNCTION `getTableColumns`(_schemaName varchar(100), _tableName varchar(100), _omitColumns varchar(200)) RETURNS varchar(5000) CHARSET latin1
BEGIN
SELECT GROUP_CONCAT(COLUMN_NAME) FROM information_schema.columns
WHERE table_schema = _schemaName AND table_name = _tableName AND FIND_IN_SET(COLUMN_NAME,_omitColumns) = 0 ORDER BY ORDINAL_POSITION;
END
Create and execute select statement:
SET #sql = concat('SELECT ', (SELECT
functions.getTableColumns('test', 'employees', 'age,dateOfHire')), ' FROM test.employees');
PREPARE stmt1 FROM #sql;
EXECUTE stmt1;
2. OR without writing a function you could:
SET #sql = CONCAT('SELECT ', (SELECT GROUP_CONCAT(COLUMN_NAME) FROM
information_schema.columns WHERE table_schema = 'test' AND table_name =
'employees' AND column_name NOT IN ('age', 'dateOfHire')),
' from test.eployees');
PREPARE stmt1 FROM #sql;
EXECUTE stmt1;
*Replace test with your own schema name
**Replace employees with your own table name
***Replace age,dateOfHire with the columns you want to omit (you can leave it blank to return all columns or just enter one column name to omit)
** **You can adjust the lengths of the varchars in the function to meet your needs
The only way to do that that I know if is to enumerate each column you do want... no negative filters that I'm aware of.
select name, age, surname, lastname, category from table
you can't do that, sorry. Actually you shouln't have done it if you could - specifying these things explicitly is always better, assume other developer adds new field and your application will fail
You are too advanced.
The only data language that I have seen that supports your syntax is the D language with its "...ALL BUT ..." construct:
Wikipedia - D Language Specification
There are some reference implementations available, but mostly for teaching purposes.
Unless there's some special extension in MySql you cannot do that. You either get all, or have to explicitly state what you want. It is best practice to always name columns, as this will not alter the query behaviour even if the underlying table changes.
There is no SQL syntax to support:
select * from table but not select id,active
If you want all but one or more columns, you have to explicitly define the list of columns you want.
You should not be using select * anyway. Enumerate the columns you want and only the columns you want, that is the best practice.
SET #sql = CONCAT('SELECT ',
(SELECT REPLACE(GROUP_CONCAT(COLUMN_NAME), '<columns_to_delete>,', '')
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = '<table>'
AND TABLE_SCHEMA = '<database>'),
' FROM <table>');
PREPARE stmt1 FROM #sql;
EXECUTE stmt1;
I'm fairly certain you can't. Probably the best way I can think of is to create SELECT name, age, surname, lastname, category FROM table as a view, then just SELECT * FROM view. I prefer to always select from a view anyway.
However, as others have pointed out, if another column gets added to the view your application could fail. On some systems as well (PostgreSQL is a candidate) you cannot alter the table without first dropping the view so it becomes a bit cumbersome.
If the reason is to avoid column duplication error without having to specify a long list of columns:
temporarily change the name of column that is a duplicate to enable the view to be created.
delete the duplicate column from the select and save view
rename the changed column name
If the reason is simply to omit a one or more columns:
create view and delete column/s from select