I have a table in mysql like this (the id is primary key):
id | name | age
1 | John | 46
2 | | 56
3 | Jane | 25
Now I want to update the name only if this is empty. If the value is not empty it should duplicate the row with a new id else it should update the name.
I thought it could be done with an if-statement but it doesn't work.
if((select `name` from `table1` where `id` = 3) = '',
update `table1` set `name`='ally' where `id` = 3,
insert into `table1` (`id`,`name`,`age`) values
(4, 'ally', select `age` from `table1` where `id` = 3))
EDIT:
With Spencers answer I made it working using an if in the code. (However I would still like a way to do just a single mysql query).
db.set_database('database1')
cursor = db.cursor()
query = "select IF(CHAR_LENGTH(name)>0,1,0) from table1 where id = {0}".format(id)
cursor.execute(query)
val1 = cursor.fetchone()
if val1[0]:
query = "INSERT INTO `table1` (`id`,`name`,`age`) SELECT {0},{1},`age` FROM `table1` WHERE `id` = {2}".format(new_id, name, id)
cursor.execute(query)
else:
query = "update `table1` set `name` = '{0}' where `id` = {1}".format(name, id)
cursor.execute(query)
db.commit()
If you make like this :
select t.*,
if(
EXISTS(select n.name from table1 n where n.id = 2 and NULLIF(n.name, '') is null) ,
'true',
'false'
) from table1 t
if statement returns "true", becouse in your table exist row where id =2 and name is empty.
like this example, You can edit your query :
if(
EXISTS(select n.name from table1 n where n.id = 3 and NULLIF(n.name, '') is null),
update `table1` set `name`='ally' where `id` = 3,
insert into `table1` (`id`,`name`,`age`) values
(4, 'ally', select `age` from `table1` where `id` = 3)
)
IF is not a valid MySQL statement (outside the context of a MySQL stored program).
To perform this operation, you'll need two statements.
Assuming that a zero length string and a NULL value are both conditions you'd consider as "empty"...
To conditionally attempt an update of the name field, you could do something like this:
UPDATE table1 t
SET t.name = IF(CHAR_LENGTH(t.name)>0,t.name,'ally')
WHERE t.id = 3 ;
The IF expression tests whether the current value of the column is "not empty". If it's not empty, the expression returns the current value of the column, resulting in "no update" to the value. If the column is empty, then the expression returns 'ally'.
And you'd need a separate statement to attempt an INSERT:
EDIT
This isn't right, not after a successful UPDATE... of the existing row. The attempt to INSERT might need to run first,
INSERT INTO table1 (id,name,age)
SELECT 4 AS id, 'ally' AS name, t.age
FROM table1 t
WHERE t.id = 3
AND CHAR_LENGTH(t.name)>0;
We need a conditional test in the WHERE clause that prevents a row from being returned if we don't need to insert a row. We don't need to insert a row if the value 'ally' ...
The use of CHAR_LENGTH >0 is a convenient test for string that is not null and is not zero length. You could use different test, for however you define "empty". Is a single space in the column also considered "empty"?)
Related
I want to get null value for each element if there is no match in the in operator value, e.g.
SELECT name
FROM table
WHERE id IN (1, 2, 3, 4, 5)
In such a case I want to get null value in case IN operator value does not exists, e.g. for
IN (1, 2, 3, 4, 5)
I want return value as
'ABC', null, 'XYZ', null, null
Currently it just returns
'ABC', 'XYZ'
Just create helper table with all values:
create table helpTable (uid int);
insert into helpTable values (1),(2),(3),(4),(5);
and then left join it and use case statement to determine if there should be displayed name when there's corresponding value (not null):
SELECT case when ht.uid is not null then name end `name`
FROM table t
LEFT JOIN helpTable ht ON t.id = ht.uid;
After you are done, DROP TABLE IF EXISTS helpTable;.
If you don't want to create table, you could do this (but I am not 100% sure that it'll work):
SELECT case when ht.uid is not null then name end `name`
FROM table t
LEFT JOIN (
select 1 `uid`
union all
select 2
union all
select 3
union all
select 4
union all
select 5
) ht ON t.id = ht.uid;
This predicate:
WHERE id IN (1, 2, 3, 4, 5)
Can only be applied to rows that exist in the table. That's how the WHERE clause works, it is evaluated for rows that exist, and if the conditions are true, that row is included in the result set of the query.
A predicate cannot invent new rows that don't exist in your table.
So no, you can't use IN ( ) to insert NULL rows where the id is missing in your table.
You can create temporary table, e.g.:
create tmp_table (name varchar2(32);
and use this pl/sql procedure:
declare
name_tmp varchar2(32);
for i in (SELECT * FROM table WHERE id IN (1, 2, 3, 4, 5)) loop
SELECT name name_tmp into FROM table id=i.id;
exception when no_data_found then name_tmp := null; end;
insert into tmp_table (name) values (name_tmp) ;
end loop;
end;
I want to ignore duplicates (or update key on duplicates), but only on consecutive duplicates.
Say my initial table looks like this (uk and a are column names):
uk a
----
x 1
Iteration 1 inserts 1 so I want this to be ignored in order to avoid duplicates.
Iteration 2 inserts 2 and my table now looks like this:
uk a
----
x 1
x 2
Iteration 3 inserts 1 and because the last row where my unique key = x was different to 1 then I want 1 to be inserted again:
uk a
----
x 1
x 2
x 1
How can this be achieved in MySQL?
My current solution is to query in data from the table first and remove these duplicates, but I would prefer that it was handled by MySQL.
You need a column that will be an auto-increment primary key
CREATE TABLE your_table(
id INT NOT NULL AUTO_INCREMENT,
uk CHAR(30) NOT NULL,
a INT,
PRIMARY KEY (id)
);
INSERT INTO your_table(uk, a) VALUES ('x', 1)
now you can use the following insert command to avoid duplicates as you describe it
INSERT INTO your_table(uk, a)
SELECT 'x', 1 FROM your_table t1
JOIN (
SELECT max(id) maxid, uk
FROM your_table
GROUP BY uk
) t ON t.maxid = t1.id and
t1.a != 1 and
t.uk = 'x'
The second insert can be the following
INSERT INTO your_table(uk, a)
SELECT 'x', 2 FROM your_table t1
JOIN (
SELECT max(id) maxid, uk
FROM your_table
GROUP BY uk
) t ON t.maxid = t1.id and
t1.a != 2 and
t.uk = 'x'
The third insert will be the same as the first one and the result table will be as expected.
See the demo
From your table it is not visible, which values got inserted last. Data in a table is considered unordered. So for your last example we know there are the three records x|1, x|1, x|2 in the table, but not whether x|1 or x|2 was inserted last. You'd need an additional column to indicate this. This could be a datetime or an ascending ID.
If you don't want to change your table, you need a helper table containing the last record instead. Anyway, you'd write a before-insert trigger to look up the last inserted value and throw an error when the new record matches the last inserted record for th uk.
create table mytable(uk char(1), a int);
create table mytable_helper(uk char(1), a int, primary key (uk));
create trigger mytable_ins BEFORE INSERT ON mytable FOR EACH ROW
begin
DECLARE msg VARCHAR(128);
if exists (select * from mytable_helper h where new.uk = h.uk and new.a = h.a) then
set msg = concat('Consecutive duplicate for a = ', new.a, ' and uk = ''', new.uk, '''.');
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = msg;
else
replace into mytable_helper (uk, a) values (new.uk, new.a);
end if;
end;
insert into mytable (uk, a) values ('x', 1);
insert into mytable (uk, a) values ('x', 2);
insert into mytable (uk, a) values ('x', 1);
insert into mytable (uk, a) values ('y', 3);
insert into mytable (uk, a) values ('x', 1);
Error(s), warning(s):
Consecutive duplicate for a = 1 and uk = 'x'.
REXTESTER http://rextester.com/XOG65602
I've being trying to amend the solution found in this tutorial to write an SQL query that both SELECTs and UPDATEs my table:
enter link description here
DECLARE #column1 varchar(2);
SET #column1 = (SELECT `Id`, `Url` FROM `MyTable` WHERE `Retrieved` = 0);
SELECT * FROM `MyTable` WHERE `AdId`, `Url` = #column1;
UPDATE `MyTable` SET `Retrieved` = 1 where `Id`, `Url` = #column1;
What i'm trying to achieve the following simultaneously:
SELECT Id, Url FROM MyTable WHERE Retrieved = 0
UPDATE MyTable SET Retrieved = 1
for the rows where i have SELECTed the results from
Basically, i want to select all data from ID and Url columns where the Retrieved column equals 0. I then want to set the Retrieved column to 1 for the rows i have selected.
The "normal" SQL method would be:
UPDATE MyTable
SET Retrieved = 1
WHERE id IN (SELECT Id FROM MyTable WHERE Retrieved = 0);
That does not work in MySQL. Assuming that id is unique in MyTable (a reasonable assumption in my opinion), then this does what you want:
UPDATE MyTable
SET Retrieved = 1
WHERE Retrieved = 0;
UPDATE t SET t.Retrieved=1 FROM MyTable t WHERE t.Retrieved=0
This is only updating rows, that essentially you've selected. In your case you want to update rows where the selected rows Retrieved column is equal to 0.
The other thing and maybe for readibility or you need the rows returned you can use a cte.
--first get only the records you need
WITH MyRecords_cte
AS
(
SELECT Id, URL, Retreived FROM MyTable WHERE Retreived=0
)
UPDATE MyRecords_cte SET MyRecords_cte.Retreived=1
Once you're done with the update you can return the data.
I'm working on following query:
INSERT INTO a (id, value) VALUES (_id, (SELECT value FROM b WHERE b.id = _id));
Table a: id, value (has a default value)
Table b: id, value
Table b does not contain all requested values. So the SELECT query sometimes returns 0 rows. In this case it should use the default value of a.value.
Is this somehow possible?
Edit:
Solution for empty columns in comments of the post marked as solved.
you can wrap the value in coalesce(max(value), default_value)
INSERT INTO a (id, value)
VALUES (_id, (SELECT coalesce(max(value), default_value)) FROM b WHERE b.id = _id));
The following query would work. First the max(value) is looked up from table b for _id. It would be either NULL or equal to b.value. If it is NULL (checked using the COALESCE function), then the default value of the value column of table a is set as the value.
The default value of the value column of table a is accessed using the DEFAULT function (please refer Reference 1).
INSERT INTO a
SELECT
_id,
COALESCE(max(value), (SELECT DEFAULT(value) FROM a LIMIT 1)) value
FROM b
WHERE id = _id;
SQL Fiddle demo
Reference:
How to SELECT DEFAULT value of a field on SO
If MySQL follows other RDBMS behaviour, the default is only picked up when you don't even specify the field. This means that you need two different INSERT statements:
IF (EXISTS(SELECT * FROM b WHERE id = _id)) THEN
INSERT INTO a (id, value) SELECT _id, value FROM b WHERE id = _id;
ELSE
INSERT INTO a (id) SELECT _id;
END IF;
Or, possibly, something like this...
INSERT INTO a (id, value) SELECT _id, value FROM b WHERE id = _id;
IF ((SELECT ROW_COUNT()) = 0) THEN
INSERT INTO a (id) SELECT _id;
END IF;
Please note, this is conceptual. I've looked up the syntax for you, but I haven't tested it on MySQL.
I've a Table with 10 fields. Each field or column contains Integer values.
Now I need only field(s) to be returned which has Null or 0 in the result set.
This worked for me. For instance:
Instead of:
where column_name is null or column_name = 0
It would be:
where COALESCE(column_name,0) = 0
Posted by Jon Gabrielson on December 18, 2002
The function 'COALESCE' can simplify working with null values. for
example, to treat null as zero, you can use: select
COALESCE(colname,0) from table where COALESCE(colname,0) > 1;
Use where column_name is null or column_name = 0
It's not clear what you are asking.
Can you elaborate a bit on what the resultset should look like, do you want all 10 columns returned, but only include the rows that have at least one column containing NULL or 0? That's very easy to do, by specifying appropriate predicates in the WHERE clause.
SELECT col0, col1, col2, col3, col4, col5, col6, col7, col8, col9
FROM mytable
WHERE IFNULL(col0,0) = 0
OR IFNULL(col1,0) = 0
OR IFNULL(col2,0) = 0
OR IFNULL(col3,0) = 0
OR IFNULL(col4,0) = 0
OR IFNULL(col5,0) = 0
OR IFNULL(col6,0) = 0
OR IFNULL(col7,0) = 0
OR IFNULL(col8,0) = 0
OR IFNULL(col9,0) = 0
That will return all rows that have a zero or NULL in at least one of the specified columns.
But your question seems to be asking about something a little bit different; you seem to be asking about returning only certain columns based on conditions. The columns to be returned in the result set are determined by the list of expressions following the SELECT keyword. You can't dynamically alter the expressions in the SELECT list based on the values the column contain.
To return the names of the columns which have at least one row that contains a NULL or zero in that column, you could write a query like this (this is limited to 5 columns, could be easily extended to 10 or more columns):
SELECT 'col0' AS col_name FROM mytable WHERE IFNULL(col0,0) = 0
UNION SELECT 'col1' FROM mytable WHERE IFNULL(col1,0) = 0
UNION SELECT 'col2' FROM mytable WHERE IFNULL(col2,0) = 0
UNION SELECT 'col3' FROM mytable WHERE IFNULL(col3,0) = 0
UNION SELECT 'col4' FROM mytable WHERE IFNULL(col4,0) = 0
(That query is going to do some serious scanning through the table. If indexes are available, the predicates can be rewritten to allow for index range scan.)
Here's a way to to the column_names in a single row. (A NULL in one of the columns would mean that the column does not contain any zeros or NULL.)
SELECT (SELECT 'col0' FROM mytable WHERE IFNULL(col0,0)=0 LIMIT 1) AS col0
, (SELECT 'col1' FROM mytable WHERE IFNULL(col1,0)=0 LIMIT 1) AS col1
, (SELECT 'col2' FROM mytable WHERE IFNULL(col2,0)=0 LIMIT 1) AS col2
, (SELECT 'col3' FROM mytable WHERE IFNULL(col3,0)=0 LIMIT 1) AS col3
, (SELECT 'col4' FROM mytable WHERE IFNULL(col4,0)=0 LIMIT 1) AS col4
But it would be much faster to do a single scan through the table:
SELECT IF(c0>0,'col0',NULL)
, IF(c1>0,'col1',NULL)
, IF(c2>0,'col2',NULL)
, IF(c3>0,'col3',NULL)
, IF(c4>0,'col4',NULL)
FROM ( SELECT SUM(IF(IFNULL(col0,0)=0,1,0)) AS c0
, SUM(IF(IFNULL(col1,0)=0,1,0)) AS c1
, SUM(IF(IFNULL(col2,0)=0,1,0)) AS c2
, SUM(IF(IFNULL(col3,0)=0,1,0)) AS c3
, SUM(IF(IFNULL(col3,0)=0,1,0)) AS c4
FROM mytable
)
Do you need the columns that have NULL or 0, or the rows with a column that is NULL or 0, or the rows where all columns are either NULL or 0?
But I doubt you would have 10 equivalent columns in a well-designed table, so you might want to re-design your data model -- make the columns rows in another table, and join the two tables together.
You might have something like this:
CREATE TABLE a(a_id int primary key, b0 int, b1 int, b2 int, ..., b9 int );
But you want this:
CREATE TABLE a( a_id int primary key );
CREATE TABLE b( b_id int primary key );
INSERT INTO b (b_id) values (0), (1), ..., (9);
CREATE TABLE ab (
a_id int, b_id int,
v int not null, -- this would be the value of b0, b1, ...
foreign key (a_id) references a(a_id),
foreign key (b_id) references b(b_id),
primary key(a_id, b_id)
);
And then you can write something like:
SELECT * FROM a, b -- cross join to get all possible combinations
WHERE (a_id, b_id) NOT IN (
-- then remove the ones that have a value
SELECT a_id, b_id FROM a JOIN ab ON a.id = ab.a_id JOIN b ON ab.a_id
WHERE ab.v <> 0);
The last line is unnecessary if, before running it, you delete all 0'd lines:
DELETE FROM ab WHERE v = 0;
I recently had to make a similar method in another language. The logic was to check that all "columns" in a record were "true" before it could be processed.
The way I got around it was with this logic:
# loop through the records
for ($i=0; $i<count($records); $i++) {
# in each record, if any column is 0 or null we will disgard it
# this boolean will tell us if we can keep the record or not
# default value is 'true', this gives it a chance to survive
$keepit = true;
# loop through each column
for ($j=0; $j<count($records[$i]); $j++) {
# add your boolean condition (true=go ahead, false=cant go ahead)
$goahead = (!is_null($records[$i][$j]) && $records[$i][$j] != 0);
# convert the boolean to a 0 or 1,
# find the minimum number in a record
# (so if any field in a record is false i.e. 0 or null, dont use the record)
$keepit = min(+$goahead, +$keepit);
}#end of col loop
if ($keepit) {
# keep the record
array_push($anotherArray, $records[$i]);
}
}
I've written this in PHP for you. It's probably doable in MySQL too but I'd recommend you fetch all of the records, process them in PHP, and send them back to MySQL.
In future, you should design your database table so that invalid records are not allowed/not stored in the first place.