mysql join unrelated tables - mysql

I have 2 table:
create table words(id int(3) not null primary key auto_increment, name varchar(10) not null);
insert into words values(null, 'rock');
insert into words values(null, 'rick');
insert into words values(null, 'red');
insert into words values(null, 'black');
create table letters(id int(3) not null primary key auto_increment, name char(1) not null, lang char(2) not null);
insert into letters values(null, 'A', 'en');
insert into letters values(null, 'B', 'en');
insert into letters values(null, 'C', 'es');
Now, to get all words that begin with 'r' I do:
select * from words where name like 'r%';
If I want, at the same time, also get a list of all the letters with the lang = 'en', how would the query look like?
I have tried with union, but it seems you only can use it for table with the same columns.
For each row in the result list, I want to get id (from words), name (from words) AND the concatenated list of all letters that meet the criteria lang = 'en'.
I guess I need to use join, but cant really figure out how it would look like. Is there anyone can give me some help?

SELECT w.id, w.name,
(
SELECT GROUP_CONCAT(name ORDER BY name SEPARATOR ',')
FROM letters
WHERE lang = 'en'
)
FROM words
Note that this letter list will be the same for each record. Do you really need it in the dataset? You might want to consider retrieving it in a separate query and storing on the client side.

Related

Is it possible in mysql to dynamically translate IDs stored in a string?

I'm importing comment data in mysql from one database to another where some data may already exist. Relevant here, our comments table has a column display_text of type text. Inside this column we have mentions to other users stored in the format "#{{"user":id}}#" where id is a number representing the user's id in another table. It is possible for a single comment to have multiple mentions needing updated ids as well.
Since these users may already exist in the new database, part of our import process populates an id_translations table with old_id and new_id columns mapping the user's id in the source database to their id in the new database. Is it possible to write a mysql UPDATE statement to find the format of our mentions, and replace only the id with the new id?
I already have a method for selecting only the comments that will need ids updated, so statements can be written as though they would update the entire table.
MySQL version 5.6. Here's a sample of what my tables look like.
CREATE TABLE IF NOT EXISTS `comments` (
`id` int(6) unsigned NOT NULL,
`display_text` text NOT NULL,
PRIMARY KEY (`id`)
);
CREATE TABLE IF NOT EXISTS `id_translations` (
`old_id` int(6) unsigned NOT NULL,
`new_id` int(6) unsigned NOT NULL,
PRIMARY KEY (`old_id`, `new_id`)
);
INSERT INTO `comments` (`id`, `display_text`) VALUES
(1, 'Hey #{{"user":12}}#, look at this'),
(2, 'Thats pretty cool'),
(3, '#{{"user":41}}# could you take a peek at this?');
INSERT INTO `id_translations` (old_id, new_id) VALUES (12, 100), (41, 101);
The goal would be for comments to look like:
1, 'Hey #{{"user":100}}#, look at this'
2, 'Thats pretty cool'
3, '#{{"user":101}}# could you take a peek at this?'
CREATE PROCEDURE replacing ()
BEGIN
REPEAT
UPDATE comments
SET display_text = CONCAT( LEFT(display_text, LOCATE('#{{"user":', display_text) - 1),
CHAR(0),
( SELECT new_id
FROM id_translations
WHERE old_id = 0 + SUBSTRING(display_text FROM LOCATE('#{{"user":', display_text) + 10)),
CHAR(1),
SUBSTRING(display_text FROM LOCATE('}}#', display_text) + 3))
WHERE LOCATE('#{{"user":', display_text);
UNTIL !ROW_COUNT() END REPEAT;
UPDATE comments
SET display_text = REPLACE(REPLACE(display_text, CHAR(0), '#{{"user":'), CHAR(1), '}}#');
END
DEMO fiddle

MySQL group by fill gaps

I need to group by a field and fill missing information if any.
For example, we have a test table:
CREATE TEMPORARY TABLE `test` (
`name` VARCHAR(100), `description` VARCHAR(100)
);
For this table, we have the following records:
INSERT INTO `test` (`name`, `description`, `email`)
VALUES ('John', '', ''),
VALUES ('John', 'Description #1', ''),
VALUES ('John', 'Description #2', ''),
VALUES ('John', '', 'john#example.com'),
VALUES ('John', '', '');
I need to select all entries on this table grouped by name and filling gaps, such as description (in this case, it should use 'Description #2' as it is the latest non-empty value for description. Same goes for email, it should return 'john#example.com'.
How should I select these values?
PS: the actual table have several columns, so it would be good to not modify the SELECT statement.
My current select is:
SELECT `name`, `description` FROM `test` GROUP BY `name`;
The problem is it will always use the first occurrence values. I need to "merge" all values based on latest non-empty insertion.
Each column may end up using values from different entries.
Expected output:
____________________________________________
| name | description | email |
--------------------------------------------
| John | Description #2 | john#example.com |
--------------------------------------------
Thanks.
You'll have to make explicit what does "last" actually mean. The records you inserted don't have any specific order, so you'll have to add either an autoincrementing id, or something like created_at date.
Then, you can choose the right records using:
SELECT `name`, `description`
FROM `test`
GROUP BY `name`
HAVING `created_at` = MAX(`created_at`)
For the non-empty part, you'll have to filter them using WHERE description<>''.

I unable to Insert a value from a char that has been CAST as Integer and added by 1

I convert an id which is in a char column datatype. after that, I want to add it by 1 (plus 1).
Could you help me? why my query is not working?
query:
INSERT INTO `countries` (`id`, `country_name`) VALUES ((SELECT MAX(CAST(`id` as INTEGER)) AS `max_id` FROM `countries`) + 1, 'India');
The following would run:
INSERT INTO `countries` (`id`, `country_name`)
SELECT MAX(CAST(`id` as INTEGER)) + 1, 'India'
FROM `countries`;
But I think it would be easier if you just make the id column an AUTO_INCREMENT.
This is not how you should be doing identifiers.
If you want incrementing id values, you want to use the AUTO_INCREMENT feature when creating your table.
Your way is dangerous, there's always a possibility of two transactions running at the same time picking the same "next ID".
Just create a table with the flag on:
CREATE TABLE countries (
id MEDIUMINT NOT NULL AUTO_INCREMENT,
name CHAR(30) NOT NULL,
PRIMARY KEY (id)
);
INSERT INTO countries (`name`) VALUES ('India');

Using SQL Sub-queries in an INSERT Statement

Here are the two tables created:
CREATE TABLE category_tbl(
id int NOT NULL AUTO_INCREMENT,
name varchar(255) NOT NULL,
subcategory varchar(255) NOT NULL,
PRIMARY KEY(id),
CONSTRAINT nameSubcategory UNIQUE KEY(name, subcategory)
) ENGINE=InnoDB;
CREATE TABLE device(
id INT NOT NULL AUTO_INCREMENT,
cid INT DEFAULT NULL,
name VARCHAR(255) NOT NULL,
received DATE,
isbroken BOOLEAN,
PRIMARY KEY(id),
FOREIGN KEY(cid) REFERENCES category_tbl(id)
) ENGINE=InnoDB;
Below is the instruction that was given to me:
-- insert the following devices instances into the device table (you should use a subquery to set up foriegn keys referecnes, no hard coded numbers):
-- cid - reference to name: phone subcategory: maybe a tablet?
-- name - Samsung Atlas
-- received - 1/2/1970
-- isbroken - True
I'm getting errors on the insert statement below from attempting to use a sub-query within an insert statement. How would you solve this issue?
INSERT INTO devices(cid, name, received, isbroken)
VALUES((SELECT id FROM category_tbl WHERE subcategory = 'tablet') , 'Samsung Atlas', 1/2/1970, 'True');
You have different table name in CREATE TABLE and INSERT INTO so just choose one device or devices
When insert date format use the good one like DATE('1970-02-01')
When insert boolean - just TRUE with no qoutes I beleive.
http://sqlfiddle.com/#!9/b7180/1
INSERT INTO devices(cid, name, received, isbroken)
VALUES((SELECT id FROM category_tbl WHERE subcategory = 'tablet') , 'Samsung Atlas', DATE('1970-02-01'), TRUE);
It's not possible to use a SELECT in an INSERT ... VALUES ... statement. The key here is the VALUES keyword. (EDIT: It is actually possible, my bad.)
If you remove the VALUES keyword, you can use the INSERT ... SELECT ... form of the INSERT statement statement.
For example:
INSERT INTO mytable ( a, b, c) SELECT 'a','b','c'
In your case, you could run a query that returns the needed value of the foreign key column, e.g.
SELECT c.id
FROM category_tbl c
WHERE c.name = 'tablet'
ORDER BY c.id
LIMIT 1
If we add some literals in the SELECT list, like this...
SELECT c.id AS `cid`
, 'Samsung Atlas' AS `name`
, '1970-01-02' AS `received`
, 'True' AS `isBroken`
FROM category_tbl c
WHERE c.name = 'tablet'
ORDER BY c.id
LIMIT 1
That will return a "row" that we could insert. Just precede the SELECT with
INSERT INTO device (`cid`, `name`, `received`, `isbroken`)
NOTE: The expressions returned by the SELECT are "lined up" with the columns in the column list by position, not by name. The aliases assigned to the expressions in the SELECT list are arbitrary, they are basically ignored. They could be omitted, but I think having the aliases assigned makes it easier to understand when we run just the SELECT portion.

Generate unique and random code digit with MySQL

Initial goal:
I would like to generate random and unique codes (6 digits) in a table.
I use a SQL query like this one to do that:
SELECT SUBSTRING(CRC32(RAND()), 1, 6) as myCode
FROM `codes`
HAVING myCode NOT IN (SELECT code FROM `codes`)
I asked me about how it will react when there will be no more available codes so I do the following test
Test context:
MySQL version: 5.5.20
MySQL Table:
CREATE TABLE `codes` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`code` VARCHAR( 10 ) NOT NULL ,
UNIQUE (
`code`
)
) ENGINE = InnoDB;
Initial data:
INSERT INTO `codes` (`id`, `code`)
VALUES (NULL, '1'), (NULL, '2'), (NULL, '3'), (NULL, '4'), (NULL, '5'), (NULL, '6'), (NULL, '7'), (NULL, '8');
SQL Query:
SELECT SUBSTRING(CRC32(RAND()), 1, 1) as myCode
FROM `codes`
HAVING myCode NOT IN (SELECT code FROM `codes`)
By execute this query, I expect that it will always return 9 because it is the only code of one digit which does not exists.
But the result is:
Sometime it return any rows
Sometime it return rows with values that already exists
I don't understand this behavior so if someone can help :)
So the big question is:
How MySQL can return rows with values that already exists?
Thanks
I would fill a sequencetable table with all the possible values, in sequence.
Then the random query just randomly selects records from the sequencetable, and each time it picks a record it deletes it. This way you will surely get all the numbers, without wasting time in finding a "hole" number (not already picked up).
CREATE TABLE `sequencetable`
(
`sequence` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`sequence`)
)
ENGINE=InnoDB
AUTO_INCREMENT=1;
Fill the sequence (no need for the AUTOINCREMENT actually).
DECLARE i INT;
SET i=1;
REPEAT
INSERT INTO sequencetable VALUES (i);
SET i=i+1;
UNTIL i>999999 END REPEAT;
Select a random record from the sequence (do this in a loop until records are available):
DECLARE sequencen INT;
SET sequencen =
(SELECT sequence FROM sequencetable ORDER BY RAND() LIMIT 1);
DELETE FROM sequencetable WHERE sequence = sequencen;