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<>''.
Related
This question already has an answer here:
Overlapping Booking Query
(1 answer)
Closed 4 years ago.
I have a similiar problem with my sql statement like here Room Booking Query.
the following query works if an apartment has only one booking. but if an apartment has more than one booking, this apartment is also in the result, although it is not available in the requested time range.
SELECT DISTINCT `apartment`.*
FROM `apartment` `apartment`
LEFT JOIN `booking` `booking` ON `apartment`.`uid` = `booking`.`apartment`
WHERE (
NOT(
( `booking`.`start` <= '2018-07-23')
AND
( `booking`.`end` >= '2018-07-21')
)
)
Can someone help me please to write the right sql?
UPDATE:
According the hint of Matt Raines i added a field apartment, with the uid of the apartment, to the booking table. I'm very thankful for any suggestion which helps me to write the right SQL statement!
Here the UPDATED Demo Data:
--
-- Table structure for table `apartment`
--
CREATE TABLE `apartment` (
`uid` int(11) NOT NULL,
`title` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
`bookings` int(10) UNSIGNED NOT NULL DEFAULT '0'
)
--
-- Data for table `tx_apartments_domain_model_apartment`
--
INSERT INTO `apartment` (`uid`, `title`, `bookings`) VALUES
(1, 'Apartment 1', 2),
(2, 'Apartment 2', 1),
(3, 'Apartment 3', 1),
(4, 'Apartment 4', 1);
--
-- Table structure for table `booking`
--
CREATE TABLE `booking` (
`uid` int(11) NOT NULL,
`start` date DEFAULT '0000-00-00',
`end` date DEFAULT '0000-00-00',
`apartment` int(10) UNSIGNED NOT NULL DEFAULT '0'
)
--
-- Data for table `booking`
--
INSERT INTO `booking` (`uid`, `start`, `end`, `apartment`) VALUES
(1, '2018-07-18', '2018-07-20', 1),
(2, '2018-07-21', '2018-07-23', 1),
(3, '2018-07-18', '2018-07-20', 2);
You are off track thinking this has to do with multiple rows from the join. The problem is with your logic in the WHERE clause. You don't say what you are wanting in terms of the dates, so it is impossible to know what the solution should be.
I simplified down to just looking at the booking table. I get the two rows where you are expecting only one. All you need to do is figure out the conditional that you really want.
mysql> SELECT * FROM booking WHERE NOT(start <= '2018-07-23' AND end >= '2018-07-21');
+-----+------------+------------+-----------+
| uid | start | end | apartment |
+-----+------------+------------+-----------+
| 1 | 2018-07-18 | 2018-07-20 | 1 |
| 3 | 2018-07-18 | 2018-07-20 | 2 |
+-----+------------+------------+-----------+
2 rows in set (0.00 sec)
I think you are looking for a list of apartments that do not have any bookings in the date range in question.
Your query, instead, looks for apartments that have at least one booking which is not in the date range in question.
The answers to the question you have linked to should work, but you could also try reversing the question to find apartments that do have a booking in the date range. Then use a LEFT JOIN and a WHERE booking.uid IS NULL to filter out those results.
SELECT apartment.*
FROM apartment
LEFT JOIN booking ON apartment.uid = booking.apartment
AND booking.start <= '2018-07-23' AND booking.end >= '2018-07-21'
WHERE booking.uid IS NULL
You might also want to look into adding a foreign key for that booking.apartment field. At the very least, it should be the same datatype as apartment.uid (at the moment one is an INT(10) UNSIGNED and the other is an INT(11)).
The start and end dates for the booking should probably be NOT NULL, unless you can have a booking without dates. And the apartment.bookings field now looks redundant.
Could anybody kindly guide me on correct direction for below query? It's not working under phpMyAdmin.
INSERT INTO `Setting`
(`id`, `type`, `name`, `value`, `parentId`, `createdAt`, `updatedAt`, `createdById`, `updatedById`)
VALUES
(NULL, 0, 'howItWorks', 'Some URL', NULL, NULL, NULL, -1, NULL),
(NULL, 0, 'howItWorksThumb', 'Some URL', (SELECT id FROM Setting WHERE name = 'howItWorks'), NULL, NULL, -1, NULL);
Same kind of query works under PostgreSQL.
Error I am getting: #1093 - You can't specify target table 'Setting' for update in FROM clause
Question is update to explained issues related to LAST_INSERT_ID() solutions:
To use LAST_INSERT_ID() solutions; child row should be inserting immediately after parent row.
I want to get the parentId for child row not immediately after I insert parent row. What will be the solution?
Also what if I want to add two children for same parent row?
MySQL doesn't allow you to SELECT in a subquery from the same table that you're inserting into in the main query. So you'll need to split this into two INSERT queries:
You can use LAST_INSERT_ID() to get the auto-increment ID that was assigned in the last INSERT, rather than using a subquery.
INSERT INTO `Setting` (`id`, `type`, `name`, `value`, `parentId`, `createdAt`, `updatedAt`, `createdById`, `updatedById`)
VALUES (NULL, 0, 'howItWorks', 'Some URL', NULL, NULL, NULL, -1, NULL);
INSERT INTO `Setting` (`id`, `type`, `name`, `value`, `parentId`, `createdAt`, `updatedAt`, `createdById`, `updatedById`)
VALUES (NULL, 0, 'howItWorksThumb', 'Some URL', LAST_INSERT_ID(), NULL, NULL, -1, NULL);
Unfortunately, using LAST_INSERT_ID() still doesn't allow you to combine them into a single query, because it calls the function before doing any inserts.
If you're doing the second insert later, you can do it with a normal INSERT ... SELECT ...:
INSERT INTO `Setting` (`id`, `type`, `name`, `value`, `parentId`, `createdAt`, `updatedAt`, `createdById`, `updatedById`)
SELECT NULL, 0, 'howItWorksThumb', 'Some URL', id, NULL, NULL, -1, NULL
FROM Setting
WHERE name = 'howItWorks'
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
( id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
, type TINYINT NOT NULL
, name VARCHAR(100) NOT NULL
, value VARCHAR(100) NOT NULL
, parentId INT NULL
, createdById INT NOT NULL
);
It would be sensible to bind the following into a transaction.
INSERT INTO my_table
( type
, name
, value
, createdById
)
VALUES
( 0
, 'howItWorks'
, 'Some URL'
, -1
);
INSERT INTO my_table
( type
, name
, value
, parentId
, createdById
)
SELECT 0
, 'howItWorksThumb'
, 'Some URL'
, LAST_INSERT_ID()
, -1
FROM my_table;
End of transaction
SELECT * FROM my_table;
+----+------+-----------------+----------+----------+-------------+
| id | type | name | value | parentId | createdById |
+----+------+-----------------+----------+----------+-------------+
| 1 | 0 | howItWorks | Some URL | NULL | -1 |
| 2 | 0 | howItWorksThumb | Some URL | 1 | -1 |
+----+------+-----------------+----------+----------+-------------+
see the data type of the table that you created. and see also DEFAULT on the field that you create. If ID is NULL should DEFAULT = auto_increment.
I have a table in MySQL with some data like below:
id name first_name
===============================
1 hello
2 many
3 alive
4 persons
How can I make the result look like this
id name first_name
===============================
3 alive
1 hello
2 many
4 persons
So, the sorting should be alphabetic on name and/or first_name?
The following query would do the work.
SELECT *
FROM nameTable
ORDER BY IFNULL(name,first_name)
OR
SELECT *
FROM nameTable
ORDER BY coalesce(name,first_name)
N:B: If you cannot access SQL FIDDLE.
CREATE TABLE `nameTable` (
`ID` int(11) NOT NULL AUTO_INCREMENT ,
`name` varchar(50) CHARACTER SET latin1 COLLATE latin1_general_ci NULL DEFAULT NULL ,
`first_name` varchar(50) CHARACTER SET latin1 COLLATE latin1_general_ci NULL DEFAULT NULL ,
PRIMARY KEY (`ID`)
);
INSERT INTO `nametable` VALUES ('1', 'hello', null);
INSERT INTO `nametable` VALUES ('2', null, 'many');
INSERT INTO `nametable` VALUES ('3', null, 'alive');
INSERT INTO `nametable` VALUES ('4', 'persons', null);
Note:
The main difference between the two is that IFNULL function takes two arguments and returns the first one if it's not NULL or the second if the first one is NULL.
COALESCE function can take two or more parameters and returns the first non-NULL parameter, or NULL if all parameters are null.
I found the solution, the order by should look like
order by coalesce(name,first_name)
Create a sortby column which is a concatenation of the two other columns.
SELECT id, `name`, first_name, CONCAT(IFNULL(`name`,''),IFNULL(first_name,'')) AS sortby
FROM
table1
ORDER BY sortby
use this
SELECT * FROM A ORDER BY CONCAT(name,first_name) ASC
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.
I have an INT (11) column for storing the current timestamp in seconds. The query looks like:
INSERT INTO `abc` (id, timestamp) VALUES ('', UNIX_TIMESTAMP ( NOW () ) )
I don't know why, but the date isn't changed. It doesn't matter when I send the query, the column value isn't changed. It has 1342692014 value, but I don't know why.
Is there any option or other function for timestamps? I must store dates in seconds.
You never refer to the timestamp column in your query. You only have a string:
INSERT INTO `abc` (id, 'timestamp') VALUES ('', UNIX_TIMESTAMP ( NOW () ) )
^^^^^^^^^^^
Edit:
I get this with your updated code:
ERROR 1630 (42000): FUNCTION test.NOW does not exist. Check the
'Function Name Parsing and Resolution' section in the Reference Manual
Assuming it's not still the actual code and after fixing the syntax error, I can't reproduce your results. My educated guess is that id is an auto-incremented integer primary key, your current SQL mode is making MySQL take '' as NULL and inserting a new row... But I haven't really tested this hypothesis.
My working code is this:
CREATE TABLE `abc` (
`pk` INT(10) NOT NULL AUTO_INCREMENT,
`id` VARCHAR(10) NULL DEFAULT NULL,
`timestamp` INT(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`pk`)
)
ENGINE=InnoDB;
INSERT INTO abc (id, timestamp) VALUES ('', UNIX_TIMESTAMP());
-- Wait a few seconds
INSERT INTO abc (id, timestamp) VALUES ('', UNIX_TIMESTAMP());
-- Wait a few seconds
INSERT INTO abc (id, timestamp) VALUES ('', UNIX_TIMESTAMP());
SELECT timestamp FROM abc WHERE id='';
... and returns this:
+------------+
| timestamp |
+------------+
| 1342694445 |
| 1342694448 |
| 1342694450 |
+------------+
3 rows in set (0.00 sec)