Selecting unique rows with maximum values (with conditions) - mysql

With the table and data below I am trying to get the highest effective_from values that are less than the current timestamp, per unique brand/model combination - effectively the current price per item.
CREATE TABLE things
(`id` int, `brand` varchar(1), `model` varchar(5), `effective_from` int, `price` int);
INSERT INTO things
(`id`, `brand`, `model`, `effective_from`, `price`)
VALUES
(1, 'a', 'red', 1402351200, 100),
(2, 'b', 'red', 1402351200, 110),
(3, 'a', 'green', 1402391200, 120),
(4, 'b', 'blue', 1402951200, 115),
(5, 'a', 'red', 1409351200, 150),
(6, 'a', 'blue', 1902351200, 140),
(7, 'b', 'green', 1402358200, 135),
(8, 'b', 'blue', 1902358200, 155),
(9, 'b', 'red', 1902751200, 200),
(10, 'a', 'red', 1908351200, 210),
(11, 'a', 'red', 1402264800, 660);
So far I have managed to get the row I'm looking for when I add conditions for a specific brand/model combination, but don't know how to fetch the current prices for all unique row combinations.
SELECT *
FROM things
WHERE effective_from<UNIX_TIMESTAMP()
AND brand='a'
AND model='red'
ORDER BY effective_from DESC
LIMIT 1;
If the current timestamp was 1402404432 the results should be as follows:
(1, 'a', 'red', 1402351200, 100),
(3, 'a', 'green', 1402391200, 120),
(2, 'b', 'red', 1402351200, 110),
(7, 'b', 'green', 1402358200, 135),

I guess you're after this. Advise if otherwise...
SELECT x.*
FROM things x
JOIN
( SELECT brand
, model
, MAX(effective_from) max_effective_from
FROM things
WHERE effective_from <= UNIX_TIMESTAMP()
GROUP
BY brand
, model
) y
ON y.brand = x.brand
AND y.model = x.model
AND y.max_effective_from = x.effective_from;
+------+-------+-------+----------------+-------+
| id | brand | model | effective_from | price |
+------+-------+-------+----------------+-------+
| 1 | a | red | 1402351200 | 100 |
| 2 | b | red | 1402351200 | 110 |
| 3 | a | green | 1402391200 | 120 |
| 7 | b | green | 1402358200 | 135 |
+------+-------+-------+----------------+-------+
SELECT UNIX_TIMESTAMP();
+------------------+
| UNIX_TIMESTAMP() |
+------------------+
| 1402404432 |
+------------------+

Related

Combine rows/columns from two tables - JOIN clause

In the link see my SQLFIDDLE and see b
CREATE TABLE Projects
(`p_id` int, `project_title` varchar(9), `l_id` int);
INSERT INTO Projects
(`p_id`, `project_title`, `l_id`)
VALUES
(1, 'A', 6),
(2, 'B', 6),
(3, 'C', 7),
(4, 'D', 8),
(5, 'E', 9),
(6, 'F', 10);
CREATE TABLE Locations
(`l_id` int, `title` varchar(9), `parent_id` int );
INSERT INTO Locations
(`l_id`, `title`, `parent_id`)
VALUES
(1, 'Country', 0),
(2, 'District1', 1),
(3, 'District2', 1),
(4, 'District3', 1),
(5, 'District4', 1),
(6, 'Loc 5', 2),
(7, 'Loc 6', 3),
(8, 'Loc 7', 3),
(9, 'Loc 8', 4),
(10, 'Loc 9', 4),
(11, 'Loc 10', 4),
(12, 'Loc 11', 5);
I would like to achieve this:
+------+-----------+-------------+
| L_ID | Title | count P_ID |
+------+-----------+-------------+
| 2 | District1 | 2 |
| 3 | District2 | 2 |
| 4 | District3 | 2 |
| 5 | District4 | 0 |
+----+------------+------+-------+
I have tried with INNER JOIN, LEFT OUTER JOIN. All i can achieve is like below and doesnt help me:
+------+-----------+----------------------+
| L_ID | Title | parent_id | counted |
+------+-----------+------------+---------+
| 6 | Loc 5 | 2 | 2 |
| 7 | Loc 6 | 3 | 2 |
| 9 | Loc 8 | 4 | 2 |
+---- -+-----------+------------+---------+
Locations table is a nested one, if this matters. I need to count projects that are in each District and also to get District name.
I tried:
SELECT l.*, COUNT(p.l_id) AS thecounted
FROM locations l
INNER JOIN projects p ON p.l_id = l.l_id
GROUP BY l.parent_id
and
SELECT l.*, COUNT(p.l_id) AS thecounted
FROM locations l
LEFT OUTER JOIN projects p on l.l_id = p.l_id
GROUP BY l.parent_id
Consider two joins:
select d.l_id, d.title, count(p.l_id) count_p_id
from locations d
left join locations l on l.parent_id = d.l_id
left join projects p on p.l_id = l.l_id
where d.parent_id = 0
group by d.l_id, d.title
The query starts from the list of districts (d), whose parent is 0. Then, it goes down one level to the locations (l), and looks up the corresponding projects (p). The final step is aggregation.
The solution of GMB returns this 1 row
l_id title count_p_id
1 Country 0
using this script version
select d.l_id, d.title, count(p.l_id) count_p_id
from locations d
left join locations l on l.parent_id = d.l_id
left join projects p on p.l_id = l.l_id
where d.parent_id = 0
group by d.l_id, d.title
We get the desired result with the slightly corrected condition
where d.parent_id = 1
Result:
l_id title count_p_id
2 District1 2
3 District2 2
4 District3 2
5 District4 0
Sorry for posting the answer, instead of a simple comment, which would be sufficient, but don't have enough reputation credits yet.

MySQL - selecting all rooms including partial date ranges

Im currently have room availability present which displays current open rooms for bookings given a specified date range.
I need to display the same availability but instead of displaying rooms for the FULL availability i need to show partial availability.
Eg: booking 1 is from dates 22nd to 25th (within room 4)
booking 2 is from dates 24th to 28th (within room 3)
queried booking is from 23rd till 25th
22nd 23rd 24th 25th 28th
|-----------------------|
|------------------|
|------| free space
query:
SELECT r.*
, CASE WHEN b.ref IS NULL THEN 'all' ELSE 'partial' END status
FROM roominfo r
LEFT JOIN bookroom br ON br.id = r.id
LEFT JOIN book b ON b.ref = br.ref
AND b.end_date >= '2019-11-23' AND b.start_date <= '2019-11-25'
ORDERBY r.id;
example structure & data:
CREATE SCHEMA TEST;
USE TEST;
CREATE TABLE BOOK( Ref INT NOT NULL AUTO_INCREMENT, Start_Date DATE NOT NULL, End_Date DATE NOT NULL, PRIMARY KEY(Ref));
CREATE TABLE ROOMINFO( ID INT NOT NULL AUTO_INCREMENT, `Type` VARCHAR(10) NOT NULL, Max TINYINT NOT NULL, PRIMARY KEY(ID));
CREATE TABLE BOOKROOM( Ref INT NOT NULL,ID INT NOT NULL, FOREIGN KEY (Ref) REFERENCES BOOK(Ref), FOREIGN KEY (ID) REFERENCES ROOMINFO(ID));
INSERT INTO BOOK(Start_Date, End_Date) VALUES
('2019-11-22', '2019-11-25'),('2019-11-24', '2019-11-28'),('2019-12-01', '2019-12-02'),('2019-12-01', '2019-12-06'),
('2019-12-02', '2019-12-03'),('2019-12-04', '2019-12-10'),('2019-12-04', '2019-12-10'),('2019-12-05', '2019-12-13'),
('2019-12-16', '2019-12-19'),('2019-12-26', '2019-12-28'),('2019-12-26', '2020-01-01'),('2019-12-28', '2020-01-02'),
('2019-12-31', '2020-01-05'),('2020-01-03', '2020-01-08'),('2020-01-05', '2020-01-11'),('2020-01-06', '2020-01-09'),
('2020-01-06', '2020-01-11'),('2020-01-08', '2020-01-18'),('2020-01-11', '2020-01-15'),('2020-01-15', '2020-01-17'),
('2020-01-15', '2020-01-18');
INSERT INTO ROOMINFO (ID, `Type`,Max) VALUES
(1, "Family", 4), (2, "Family", 4), (3, "Family", 4), (4, "Dual", 2),
(5, "Dual", 2), (6, "Dual", 2), (7, "Dual", 2), (8, "Dual", 2),
(9, "Dual", 2), (10, "Dual", 2);
INSERT INTO BOOKROOM( Ref, ID ) VALUES
(1, 4), (2, 3), (3, 4), (4, 5),(5, 6), (6, 7), (7, 3), (8, 2), (9, 1), (10, 8),(11, 3),
(12, 9), (13, 2), (14, 10), (15, 4), (16, 5), (17, 6), (18, 7), (19, 2),(20, 1), (21, 10);
desired output:
id (& some indication of partial availability?)
1 all
2 all
3 partial
4 partial
5 all
6 all
7 all
8 all
9 all
10 all
Ignoring the distressing naming policy...
DROP TABLE IF EXISTS book;
CREATE TABLE BOOK( Ref INT NOT NULL AUTO_INCREMENT, Start_Date DATE NOT NULL, End_Date DATE NOT NULL, PRIMARY KEY(Ref));
DROP TABLE IF EXISTS roominfo;
CREATE TABLE ROOMINFO( ID INT NOT NULL AUTO_INCREMENT, `Type` VARCHAR(10) NOT NULL, capacity TINYINT NOT NULL, PRIMARY KEY(ID));
DROP TABLE IF EXISTS bookroom;
CREATE TABLE BOOKROOM( Ref INT NOT NULL,ID INT NOT NULL);
INSERT INTO BOOK(Start_Date, End_Date) VALUES
("2019-11-03", "2019-11-10"), ("2019-11-05", "2019-11-13");
INSERT INTO ROOMINFO (ID, `Type`,capacity) VALUES
(1, "Family", 4), (2, "Family", 4), (3, "Family", 4), (4, "Dual", 2),
(5, "Dual", 2), (6, "Dual", 2), (7, "Dual", 2), (8, "Dual", 2),
(9, "Dual", 2), (10, "Dual", 2);
INSERT INTO BOOKROOM( Ref, ID ) VALUES (1, 4), (2, 3);
SELECT r.*
, CASE WHEN b.ref IS NULL THEN 'all' ELSE 'partial' END status
FROM roominfo r
LEFT
JOIN bookroom br
ON br.id = r.id
LEFT
JOIN book b
ON b.ref = br.ref
AND b.end_date >= '2019-11-01' AND b.start_date <= '2019-11-13'
ORDER
BY r.id;
+----+--------+----------+---------+
| ID | Type | capacity | status |
+----+--------+----------+---------+
| 1 | Family | 4 | all |
| 2 | Family | 4 | all |
| 3 | Family | 4 | partial |
| 4 | Dual | 2 | partial |
| 5 | Dual | 2 | all |
| 6 | Dual | 2 | all |
| 7 | Dual | 2 | all |
| 8 | Dual | 2 | all |
| 9 | Dual | 2 | all |
| 10 | Dual | 2 | all |
+----+--------+----------+---------+

Search by tags with relevance

I couldnt find anything like this
I have Sql tables
tag (tag_id, tag_name..)
tags (id, tag_id, post_id)
Example:
table: tag
1 New York
2 Madrid
3 Paris
4 London
if I have tags table populated in a manner that some posts contains 0,1,2,3 or many tags, how to make sql with search order to get result sorted by number of tags that post contains ? Like a "best match" search..
For example if I search for 3 tags "New York", "Madrid", "Paris"
the expexted result should be>
Post_id's: 1,4,6, 2,3,5, 9
Where show first
1,4,6 (has all 3 searched tags)
then
2,3,5 (has 2 searched tags)
then
9 (with 1 searched tag)
Try this:
SELECT tags.post_id, COUNT(*) AS match_count
FROM tag
JOIN tags ON tag.tag_id = tags.tag_id
WHERE tag.tag IN ('New York', 'Madrid', 'Paris')
GROUP BY tags.post_id
ORDER BY COUNT(*) DESC, tags.post_id ASC;
Gives these results:
+---------+-------------+
| post_id | match_count |
+---------+-------------+
| 1 | 3 |
| 4 | 3 |
| 6 | 3 |
| 2 | 2 |
| 3 | 2 |
| 5 | 2 |
| 9 | 1 |
+---------+-------------+
With this data:
CREATE TABLE tag (
tag_id INTEGER PRIMARY KEY AUTO_INCREMENT,
tag VARCHAR(40)
);
INSERT INTO tag VALUES
(null, 'New York'),
(null, 'Madrid'),
(null, 'Paris'),
(null, 'London');
CREATE TABLE tags (
id INTEGER PRIMARY KEY AUTO_INCREMENT,
tag_id INTEGER,
post_id INTEGER
);
INSERT INTO tags VALUES
(null, 1, 1),
(null, 2, 1),
(null, 3, 1),
(null, 1, 4),
(null, 2, 4),
(null, 3, 4),
(null, 1, 6),
(null, 2, 6),
(null, 3, 6),
(null, 1, 2),
(null, 2, 2),
(null, 1, 3),
(null, 2, 3),
(null, 1, 5),
(null, 2, 5),
(null, 1, 9);

Update a table with conditions in specfic order

I have searched the web for my problem, tested some subqueries and derived table approaches with Case Statements, but didnĀ“t get the result. Perhaps you can help? Thanks.
The examples below are just an example.
# generate the table as it is
DROP TABLE IF EXISTS `IN`;
CREATE TABLE `IN`
(`Part` CHAR(1),
`Warehouse` INT(1),
`Percentage` INT(1),
`Update` INT(1));
#some values for the table
INSERT INTO `IN`
(Part, Warehouse, Percentage)
VALUES
('A' , 1, 80),
('A', 2, 100),
('A', 3, 50),
('B', 1, 100),
('B', 2, 50),
('B', 3, 100);
# generate table as it should be
DROP TABLE IF EXISTS `OUT`;
CREATE TABLE `OUT`
(`Part` CHAR(1),
`Warehouse` INT(1),
`Percentage` INT(1),
`Update` INT(1));
# values for the table
INSERT INTO `OUT`
(Part, Warehouse, Percentage, `Update`)
VALUES
('A' , 1, 80, 2),
('A', 2, 100, 2),
('A', 3, 50, 2),
('B', 1, 100, 3),
('B', 2, 50, 3),
('B', 3, 100, 3);
I would like to add the specific warehouse name in the column Update for the specific part if the percentage is 100.
The value of the warehouse should be filled to every row for the specific part.
The fill calculation of the update column should be in a specific order.
So first there should be a check if warehouse 3 has 100 and take this value. If warehouse 3 only has 50 then check warehouse 2, if it has 100.
Thank you very much!
Here's one way...
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(Part CHAR(1)
,Warehouse INT NOT NULL
,Percentage TINYINT NOT NULL
);
INSERT INTO my_table
(Part, Warehouse, Percentage)
VALUES
('A' , 1, 80),
('A', 2, 100),
('A', 3, 50),
('B', 1, 100),
('B', 2, 50),
('B', 3, 100);
SELECT w1.*, COALESCE(w3.warehouse,w2.warehouse,w1.warehouse) warehouse
FROM my_table w1
LEFT
JOIN my_table w2
ON w2.part = w1.part
AND w2.warehouse = 2
AND w2.percentage = 100
LEFT
JOIN my_table w3
ON w3.part = w1.part
AND w3.warehouse = 3
AND w3.percentage = 100;
+------+-----------+------------+-----------+
| Part | Warehouse | Percentage | warehouse |
+------+-----------+------------+-----------+
| A | 1 | 80 | 2 |
| A | 2 | 100 | 2 |
| A | 3 | 50 | 2 |
| B | 1 | 100 | 3 |
| B | 2 | 50 | 3 |
| B | 3 | 100 | 3 |
+------+-----------+------------+-----------+

Mysql Grouping of Max field by month on a set of Fields

I would like to generate a SQL to list to display the maximum tally count based on enumerated group of values on a monthly basis. As this would be useful for analytics based algorithm in displaying the total impressions of a particular data type.
Please check my sample table:
CREATE TABLE IF NOT EXISTS `company_attendance_tally` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`start_date` datetime NOT NULL,
`sick_type` enum('VACATION','SICK','MATERNITY') COLLATE utf8_unicode_ci NOT NULL,
`leave_count` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `start_date` (`start_date`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=32 ;
--
-- Dumping data for table `company_attendance_tally`
--
INSERT INTO `company_attendance_tally` (`id`, `start_date`, `sick_type`, `leave_count`) VALUES
(1, '2013-03-01 16:58:44', 'VACATION', 5),
(2, '2013-03-15 10:44:35', 'SICK', 43),
(3, '2013-03-21 17:03:33', 'MATERNITY', 44),
(4, '2013-03-07 23:01:30', 'MATERNITY', 10),
(5, '2013-03-22 17:07:07', 'MATERNITY', 1),
(6, '2013-03-08 19:33:04', 'VACATION', 40),
(7, '2013-03-17 12:27:00', 'MATERNITY', 15),
(8, '2013-03-03 23:26:48', 'SICK', 11),
(9, '2013-03-05 02:16:37', 'MATERNITY', 41),
(10, '2013-03-20 12:04:28', 'MATERNITY', 18),
(11, '2013-03-18 02:10:00', 'MATERNITY', 1),
(12, '2013-03-03 09:47:02', 'MATERNITY', 19),
(13, '2013-03-22 10:17:52', 'MATERNITY', 25),
(14, '2013-03-03 19:41:52', 'VACATION', 10),
(15, '2013-03-02 19:28:41', 'SICK', 39),
(16, '2013-03-01 20:45:26', 'SICK', 42),
(17, '2013-03-26 23:52:16', 'MATERNITY', 29),
(18, '2013-03-29 14:10:58', 'SICK', 44),
(19, '2013-03-27 03:11:40', 'MATERNITY', 12),
(20, '2013-03-06 18:38:28', 'MATERNITY', 30),
(21, '2013-03-07 20:49:14', 'VACATION', 27),
(22, '2013-03-13 11:38:45', 'VACATION', 14),
(23, '2013-03-02 19:13:31', 'SICK', 2),
(24, '2013-03-01 10:08:18', 'SICK', 27),
(25, '2013-03-20 01:56:38', 'VACATION', 3),
(26, '2013-03-04 21:02:05', 'SICK', 7),
(27, '2013-03-17 00:47:17', 'MATERNITY', 36),
(28, '2013-03-04 08:12:56', 'VACATION', 5),
(29, '2013-03-18 08:50:57', 'SICK', 34),
(30, '2013-03-26 02:20:58', 'VACATION', 20),
(31, '2013-03-27 10:27:00', 'SICK', 21);
http://sqlfiddle.com/#!2/bbd1e3
I would like to display a similar output below based on the above scenario:
month| day | sick_type | leave_count |
-----------------------------------------------------
3| 08 | VACATION | 40
3| 29 | SICK | 29
3| 21 | MATERNITY | 44
and so on so forth...
4| ... | MATERNITY | ..
4| ... | SICK | ..
4| ... | VACATION | ..
5| ... | MATERNITY | ..
5| ... | SICK | ..
5| ... | VACATION | ..
If I understand correctly what you want you can do following by leveraging non-standard MySQL GROUP BY extension
SELECT MONTH(start_date) month,
DAYOFMONTH(start_date) day,
sick_type,
leave_count
FROM
(
SELECT start_date, sick_type, leave_count
FROM company_attendance_tally
WHERE start_date >= '2013-01-01'
AND start_date < '2014-01-01'
ORDER BY MONTH(start_date), sick_type, leave_count DESC
) q
GROUP BY MONTH(start_date), sick_type
Note: Month values alone (without a year values) in the resultset make sense only if you limit the resultset by one year boundaries (see WHERE clause).
Output:
| MONTH | DAY | SICK_TYPE | LEAVE_COUNT |
|-------|-----|-----------|-------------|
| 3 | 8 | VACATION | 40 |
| 3 | 29 | SICK | 44 |
| 3 | 21 | MATERNITY | 44 |
Here is SQLFiddle demo
Use this:
SELECT DAY( start_date ) AS d,
MONTH( start_date ) AS mon,
sick_type,
MAX( leave_count ) AS leave_count
FROM `company_attendance_tally`
GROUP BY mon, sick_type
If you want to do it 'by-the-book', consider the following...
SELECT x.*
FROM company_attendance_tally x
JOIN
( SELECT MONTH(start_date) start_month
, sick_type
, MAX(leave_count) max_leave_count
FROM company_attendance_tally
GROUP
BY MONTH(start_date)
, sick_type
) y
ON y.start_month = MONTH(x.start_date)
AND y.sick_type = x.sick_type
AND y.max_leave_count = x.leave_count;