MySQL Nested SQL with limits - mysql

I know it's not possible to use limits within nested INs, but I think there is a way to do this, I'm just not sure how.
I have a table that contains both ratings and comments (simplified for explanation)
mySingleTable:
+----+------------------+-----------+-----------+-----------------+
| id | reviewer_comment | is_rating | parent_id | reviewer_rating |
+----+------------------+-----------+-----------+-----------------+
| 1 | well done rateA | 1 | 0 | 5 Stars |
| 2 | commentAonRateA | 0 | 1 | |
| 3 | commentBonRateA | 0 | 1 | |
| 4 | commentConRateA | 0 | 1 | |
| 5 | commentDonRateA | 0 | 1 | |
| 6 | commentEonRateA | 0 | 1 | |
| 7 | commentFonRateA | 0 | 1 | |
| 8 | well done rateB | 1 | 0 | 4 Stars |
| 9 | well done rateC | 1 | 0 | 5 Stars |
| 11 | well done rateD | 1 | 0 | 3 Stars |
| 12 | well done rateE | 1 | 0 | 2 Stars |
| 13 | well done rateF | 1 | 0 | 5 Stars |
| 14 | well done rateG | 1 | 0 | 3 Stars |
| 15 | commentAonRateD | 0 | 11 | |
+----+------------------+-----------+-----------+-----------------+
So,
if is_rating = 1, its a rating.
if is_rating = 0, its a comment on the rating (its parent rating is where parent_id=id)
so this would look like:
well done rateA *****
commentAonRateA
commentBonRateA
commentConRateA
commentDonRateA
commentEonRateA
commentFonRateA
well done rateB ****
well done rateC *****
well done rateD ***
commentAonRateD
well done rateE **
well done rateF *****
well done rateG ***
What I want to do is select the newest five ratings, with the ASSOCIATED newest 5 comments, using only 1 query
So, some how join these two:
SELECT ratings.*
FROM mySingleTable as ratings
WHERE
is_rating = 1
ORDER BY timestamp DESC LIMIT 0, 5
SELECT comments.*
FROM mySingleTable as comments
Where
comments.parent_id = ratings.id
AND is_rating = 0
ORDER BY timestamp DESC LIMIT 0, 5
The 2nd query needs to somehow know about the ratings query

Please try this query which is now simplified and tested.
SELECT *
FROM
(SELECT *,
IF (group_id = #prev,
#n := #n + 1,
#n := 1 AND #prev := group_id) as position
FROM (
SELECT mySingleTable.*, group_id
FROM mySingleTable
INNER JOIN
(SELECT id AS group_id
FROM mySingleTable
WHERE is_rating = 1
ORDER BY timestamp DESC LIMIT 0, 5
) AS top5ratings
ON mySingleTable.id = group_id OR mySingleTable.parent_id = group_id
ORDER BY group_id DESC,
mySingleTable.parent_id ASC,
timestamp DESC) AS all_reviews
JOIN (SELECT #n := 0, #prev := 0) AS setup) AS reviews
WHERE reviews.position < 7
Keep in mind that SELECT * is bad practice. I used it to simplify reading.

TRY THIS QUERY
SELECT comments.*
FROM
(
SELECT ratings.id
FROM mySingleTable as ratings
WHERE
is_rating = 1
ORDER BY timestamp DESC LIMIT 0, 5 ) AS Top5
INNER JOIN mySingleTable AS comments ON comments.parent_id = Top5.id
WHERE comments.is_rating = 0
ORDER BY comments.timestamp DESC LIMIT 0, 5

Related

MySQL - Select Top 5 with Rankings

I'm trying to get a users ranking getting his highest performances in every beatmap.
I get the user highest performance in every beatmap (only taking the top 5 performances) and adding them together, but it fails when the highest performance in one beatmap is repeated... because it counts twice
I'm based in this solution, but it doesn't works well for me...
Using MySQL 5.7
What i'm doing wrong?
Fiddle
Using this code:
SET group_concat_max_len := 1000000;
SELECT #i:=#i+1 rank, x.userID, x.totalperformance FROM (SELECT r.userID, SUM(r.performance) as totalperformance
FROM
(SELECT Rankings.*
FROM Rankings INNER JOIN (
SELECT userID, GROUP_CONCAT(performance ORDER BY performance DESC) grouped_performance
FROM Rankings
GROUP BY userID) group_max
ON Rankings.userID = group_max.userID
AND FIND_IN_SET(performance, grouped_performance) <= 5
ORDER BY
Rankings.userID, Rankings.performance DESC) as r
GROUP BY userID) x
JOIN
(SELECT #i:=0) vars
ORDER BY x.totalperformance DESC
Expected result:
+------+--------+------------------+
| rank | userID | totalperformance |
+------+--------+------------------+
| 1 | 1 | 450 |
+------+--------+------------------+
| 2 | 2 | 250 |
+------+--------+------------------+
| 3 | 5 | 140 |
+------+--------+------------------+
| 4 | 3 | 50 |
+------+--------+------------------+
| 5 | 75 | 10 |
+------+--------+------------------+
| 6 | 45 | 0 | --
+------+--------+------------------+
| 7 | 70 | 0 | ----> This order is not relevant
+------+--------+------------------+
| 8 | 76 | 0 | --
+------+--------+------------------+
Actual Result:
+------+--------+------------------+
| rank | userID | totalperformance |
+------+--------+------------------+
| 1 | 1 | 520 |
+------+--------+------------------+
| 2 | 2 | 350 |
+------+--------+------------------+
| 3 | 5 | 220 |
+------+--------+------------------+
| 4 | 3 | 100 |
+------+--------+------------------+
| 5 | 75 | 10 |
+------+--------+------------------+
| 6 | 45 | 0 | --
+------+--------+------------------+
| 7 | 70 | 0 | ----> This order is not relevant
+------+--------+------------------+
| 8 | 76 | 0 | --
+------+--------+------------------+
As you have mentioned that you are picking only top 5 performances per user across beatmaps then you can try this way:
select #i:=#i+1, userid,performance from (
select userid,sum(performance) as performance from (
select
#row_number := CASE WHEN #last_category <> t1.userID THEN 1 ELSE #row_number + 1 END AS row_number,
#last_category :=t1.userid,
t1.userid,
t1.beatmapid,
t1.performance
from (
select
userid, beatmapid,
max(performance) as performance
from Rankings
group by userid, beatmapid
) t1
CROSS JOIN (SELECT #row_number := 0, #last_category := null) t2
ORDER BY t1.userID , t1.performance desc
) t3
where row_number<=5
group by userid
)
t4 join (SELECT #i := 0 ) t5
order by performance desc
Above query will not consider duplicate Performance Score and pick only top 5 performance values.
DEMO

Mysql update all rows value with count of same table column

I have following table with data:
| predp_id | strp_ID | predp_nas |
| -------- | ------- | --------- |
| 1 | 1 | null |
| 2 | 1 | null |
| 3 | 1 | null |
| 4 | 2 | null |
| 5 | 2 | null |
| 6 | 3 | null |
predp_nas column should be count of strp_ID column + 1 for same strp_ID on every row.
I am currently using next query to achieve this on every new insert:
INSERT INTO PREDMETIP
(`strp_ID`, `predp_nas`)
VALUES(
1,
(SELECT counter + 1 FROM (SELECT COUNT(strp_ID) counter FROM PREDMETIP WHERE strp_ID = '1') t)
);
This gives me:
| predp_id | strp_ID | predp_nas |
| -------- | ------- | --------- |
| 1 | 1 | null |
| 2 | 1 | null |
| 3 | 1 | null |
| 4 | 2 | null |
| 5 | 2 | null |
| 6 | 3 | null |
| 7 | 1 | 4 |
But now I have imported large amount of data and I need to update all predp_nas fields at once to give me result:
| predp_id | strp_ID | predp_nas |
| -------- | ------- | --------- |
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 1 | 3 |
| 4 | 2 | 1 |
| 5 | 2 | 2 |
| 6 | 3 | 1 |
| 7 | 1 | 4 |
I have DB fiddle with insert query View on DB Fiddle , I am having trouble understanding how to write query for same thing but to update all fields at once. Any help is appreciated.
What you're looking for is ROW_NUMBER() (if you're using MySQL 8+), but since your fiddle is on MySQL 5.7 I'm assuming that's your version and so you can emulate it by counting the number of rows for a given strp_ID that have a lower predp_id and using that to update the table:
UPDATE PREDMETIP p1
JOIN (
SELECT p1.predp_id,
COUNT(p2.predp_id) + 1 AS rn
FROM PREDMETIP p1
LEFT JOIN PREDMETIP p2 ON p2.strp_ID = p1.strp_ID AND p2.predp_id < p1.predp_id
GROUP BY p1.predp_id
) p2 ON p1.predp_id = p2.predp_id
SET p1.predp_nas = p2.rn
;
SELECT *
FROM PREDMETIP
Output after update:
predp_id strp_ID predp_nas
1 1 1
2 1 2
3 1 3
4 2 1
5 2 2
6 3 1
7 1 4
You seeem to be looking for an update query. If you are running MySQL 8.0, you can do this with row_number():
update predmetip p
inner join (
select p.*, row_number() over(partition by predp_id order by strp_id) rn
from predmetip p
) p1 on p1.predp_id = p.predp_id and p1.strp_id = p.strp_id
set p.predp_nas = p1.rn
On the other hand, if you are running a MySQL 5.x version, then one option is to use correlated subqueries, as demonstrated in Nick's answer. This works fine - and I upvoted Nick's answer - but the performance tends to quickly degrade when the volume of data gets larger, because you need to scan the table for each and every row in the resultset.
You can do this with user variables, but it's is tricky: since, as explained in the documentation, the order of evaluation of expressions in the select clause is undefined, we need to evaluate and assign in the same expression ; case comes handy for this. Another important thing is that we need to order the rows in a subquery before variables come into play.
You would write the select statement as follows:
set #rn := 0, #strp_id = '';
select
predp_id,
strp_id,
#rn := case
when #strp_id = strp_id then #rn + 1 -- read
when #strp_id := strp_id then 1 -- assign
end as predp_nas
from (
select *
from predmetip
order by strp_id, predp_id
) t
You can then turn it to an update:
set #rn := 0, #strp_id = '';
update predmetip p
inner join (
select
predp_id,
strp_id,
#rn := case
when #strp_id = strp_id then #rn + 1
when #strp_id := strp_id then 1
end as predp_nas
from (
select *
from predmetip
order by strp_id, predp_id
) t
) p1 on p1.predp_id = p.predp_id and p1.strp_id = p.strp_id
set p.predp_nas = p1.predp_nas;
Demo on DB Fiddle (with credits to Nick for creating it in the first place).
To read more about user variables and their tricks, I recommend this excellent answer by Madhur Bhaiya, which also contains another interesting blog link.

Calculate time differences between multiple set of data

This is a data set in a mysql table which is related to a error log of an electronic divice.
i need to calculate the total down time.
time_stamp error_type error_status
1467820110 1 1
1467820120 2 1
1467820130 3 1
1467820140 3 0
1467820150 1 0
1467820160 2 0
1467820180 1 1
1467820185 1 0
1467820191 2 1
1467820300 2 0
1467820302 1 1
1467820404 3 1
1467820408 3 0
1467820409 1 0
error_status 1 = error occored
error_status 0 = error fixed
1st down time 1467820160 - 1467820110 = 50
2nd down time 1467820185 - 1467820180 = 5
3rd down time 1467820300 - 1467820191 = 109
4th down time 1467820409 - 1467820302 = 107
total down time = 50 + 5 + 109 + 107 = 271
How can i write a mySQL compatible SQL statement to achieve this.
Basically, you need to calculate the number of cumulative errors that have occurred. Then identify groups where the values are greater than 0. This can be done by doing a cumulative count of the number of "0"s for the cumulative errors.
There is a challenge getting the final timestamp. One trick is to get the next status 0 timestamp for the error. This acts as an "end".
Finally, an aggregation get the information for each period:
select count(*) as num_errors, max(end_timestamp) - min(timestamp)
from (select t.*,
(#grp := #grp + if(cume_errors = 0, 1, 0)) as grp
from (select t.*,
(select t2.timestamp
from t t2
where t2.error_type = t.error_type and
t2.error_status = 0 and
t2.timestamp > t.timestamp
order by t2.timestamp asc
limit 1
) as end_timestamp,
(#e := #e + if(error_status > 0, 1, -1)) as cume_errors
from t cross join
(select #e := 0) params
order by timestamp
) t cross join
(select #grp := 0) params
order by timestamp
) t
where error_status > 0
group by grp;
You can aggregate over this query to get the total period of downtime.
Here is a SQL Fiddle.
use this you will get the total
SELECT sum(IF(error_status=1,time_stamp*-1,time_stamp)) as total FROM table;
----------example----
mysql> SELECT sum(IF(error_status=1,time_stamp*-1,time_stamp)) as total FROM hh;
+-------+
| total |
+-------+
| 315 |
+-------+
1 row in set (0.06 sec)
mysql> SELECT *,(IF(error_status=1,time_stamp*-1,time_stamp)) as total FROM hh;
+------------+------------+--------------+-------------+
| time_stamp | error_type | error_status | total |
+------------+------------+--------------+-------------+
| 1467820110 | 1 | 1 | -1467820110 |
| 1467820120 | 2 | 1 | -1467820120 |
| 1467820130 | 3 | 1 | -1467820130 |
| 1467820140 | 3 | 0 | 1467820140 |
| 1467820150 | 1 | 0 | 1467820150 |
| 1467820160 | 2 | 0 | 1467820160 |
| 1467820180 | 1 | 1 | -1467820180 |
| 1467820185 | 1 | 0 | 1467820185 |
| 1467820191 | 2 | 1 | -1467820191 |
| 1467820300 | 2 | 0 | 1467820300 |
| 1467820302 | 1 | 1 | -1467820302 |
| 1467820404 | 3 | 1 | -1467820404 |
| 1467820408 | 3 | 0 | 1467820408 |
| 1467820409 | 1 | 0 | 1467820409 |

mysql select top N records conditionally

i have the following table :
+------+------+-----+---------+------------+
| no | code |eot |group_id | compulsary |
+------+------+-----+---------+------------+
| 1005 | 101 | 51 | 1 | 1 |
| 1005 | 102 | 67 | 1 | 1 |
| 1005 | 121 | 65 | 1 | 1 |
| 1005 | 231 | 82 | 2 | 0 |
| 1005 | 232 | 56 | 2 | 0 |
| 1005 | 233 | 45 | 2 | 1 |
| 1005 | 313 | 80 | 3 | 0 |
| 1005 | 443 | 50 | 4 | 0 |
|------+------+-----+---------+------------+
now what i want is :
1.) return all records where group_id=1,
2.) return the best two from group_id=2(however if compulsary=1 then include that row and the best from the remaining group_id=2),
3.)return 1 row each from group_id=3 and group_id=4 and if compulsary=1 then return that row
the final result should have only seven rows:
+------+------+-----+---------+------------+
| no | code |eot |group_id | compulsary |
+------+------+-----+---------+------------+
| 1005 | 101 | 51 | 1 | 1 |
| 1005 | 102 | 67 | 1 | 1 |
| 1005 | 121 | 65 | 1 | 1 |
| 1005 | 231 | 82 | 2 | 0 |
| 1005 | 233 | 45 | 2 | 1 |
| 1005 | 313 | 80 | 3 | 0 |
| 1005 | 443 | 50 | 4 | 0 |
|------+------+-----+---------+------------+
with the compulsary=1 rows inluded like above;
so far I have this query though I don't know how to check for the compulsary to get what I want:
select rg.*
from
(
select *
from
(
select
rgrade.*,
#rn := if(#gr=group_id,if(#gr:=group_id,#rn+1,#rn+1),if(#gr:=group_id,1,1)) as rn
from rgrade
cross join (select #rn:=0,#gr:=0) as vars
where admission_no=1005
) v
where (group_id=1)
or (group_id=2 and if(compulsary=1,rn<=1,rn<=2))
or (group_id in (3,4) and rn=1)
) rg
order by group_id;
the query returns the seven rows as expected but does not check for compulsary in group_id=2.
Any help much appreciated
You are describing several queries here, the result of which you want combined: All group_id = 1, the two best group_id = 2, the best of group_id = 3, and the best of group_id = 4. So write these queries and combine them with UNION ALL. With "best" defined as compulsary = 1 preferred, then highest eot, you get:
(select * from mytable where group_id = 1)
union all
(select * from mytable where group_id = 2 order by compulsary = 1 desc, eot desc limit 2)
union all
(select * from mytable where group_id = 3 order by compulsary = 1 desc, eot desc limit 1)
union all
(select * from mytable where group_id = 4 order by compulsary = 1 desc, eot desc limit 1)
order by group_id, no, code
;
You are trying to mimic standard SQL's
row_number() over (partition by group_id
order by case when compulsary = 1 then 1 else 0 end desc, eot desc)
with MySQL means. And I see the partitioning by group_id in your query, but I don't see any ORDER BY to get the best records first.
Here is my attempt on it. There may be mistakes; I'm no MySQL guy.
select
no, code, eot, group_id, compulsary
from
(
select
no,
code,
eot,
compulsary,
#row_number :=
case when group_id = #last_group_id then #row_number + 1 else 1 end as row_number,
#last_group_id := group_id as group_id
from rgrade
cross join
(
select
#row_number := 0,
#last_group_id := -1
) as vars
where admission_no = 1005
order by group_id, (compulsary = 1) desc, eot desc
) ranked
where (group_id = 1)
or (group_id = 2 and row_number <= 2)
or (group_id = 3 and row_number = 1)
or (group_id = 4 and row_number = 1)
order by group_id, code;

SQL - Query multiple rooms with different Adults/Children per hotel

I have a simple query where I select available x Rooms with x Adults + x Children per hotel that matches a date range, but I'm having a hard time trying to figure out how to query a list of rooms per hotel like this:
1 Room with 2 Adults / 0 Children
1 Room with 4 Adults / 2 Children
1 Room with 2 Adults / 1 Children
Here is my query:
SELECT COUNT(pl.day) AS Days,
p.property_ID AS Hotel_ID,
p.name AS Hotel_Name,
r.room_name AS Room_Name,
r.room_type_ID AS Room_ID
FROM property p
INNER JOIN room_type r ON p.property_ID=r.property_ID
AND (r.max_adults >= 3
AND r.max_children >= 0)
INNER JOIN plan pl ON pl.room_type_ID=r.room_type_ID
AND (pl.day >= "2014-07-07"
AND pl.day <= "2014-07-11")
GROUP BY Room_ID,
Hotel_ID HAVING Days = 4
EDIT
How do I add 'No_of_Room' in SELECT that differentiates the room_types by the room number, example result of a single room:
Array
(
[Room_Price] => 160.00
[Days] => 4
[Hotel_ID] => 1
[Hotel_Name] => Hotel Alfa
[Room_Name] => Room type C
[Room_ID] => 3
[Max_Adults] => 3
[Max_Children] => 1
[No_of_Room] => 1 // What number of room does this room_type belongs to
)
Then I can show the results like:
EDIT
Rooms table
Rooms(
ID,
hotel_id
room_name,
max_Adults,
max_Children
);
-- Populate
INSERT INTO Rooms VALUES (1,1,"Room A",2,1),(2,1,"Room B",2,5),(3,1,"Room C",3,0);
INSERT INTO Rooms VALUES (1,2,"Room A",2,1),(2,2,"Room B",2,5),(3,3,"Room C",3,4);
EXAMPLES OF USING VIEWS TO MAKE THINGS NICER.
For this project authors may have aliases, for example one book may have "S. Lang" as the author, another might have "Serge Lang", the primary author is the main form (Serge Lang) and the secondaries are things like "S. Lang".
It is important to relate these, ideally I'd like a table with "AuthorId" and "PrimaryAuthorId" as columns, that way I could just select PrimaryAuthorId from it on AuthorId being equal to something.
To do this the view is defined as:
select
`BookSystem_AuthorList`.`AuthorId` AS `AuthorId`,
if((`BookSystem_AuthorList`.`duplicateOf` = 0),
`BookSystem_AuthorList`.`AuthorId`,
`BookSystem_AuthorList`.`duplicateOf`
) AS `PrimaryAuthorId`
from `BookSystem_AuthorList`;
Then
SELECT PrimaryAuthorId FROM BookSystem_PrimaryAuthorId WHERE AuthorId=10;
gives:
7
Which is much nicer for joining!
I then use this view to define another view (EditionAuthorsWithPrimaryId) - this gets the authors of an edition - and the primary author (I can then join to get names as needed)
select
`BookSystem_EditionAuthors`.`BindingId` AS `BindingId`,
`BookSystem_EditionAuthors`.`EditionId` AS `EditionId`,
`BookSystem_EditionAuthors`.`AuthorId` AS `AuthorId`,
`BookSystem_EditionAuthors`.`Position` AS `Position`,
(select
`BookSystem_PrimaryAuthorId`.`PrimaryAuthorId`
from `BookSystem_PrimaryAuthorId`
where (`BookSystem_PrimaryAuthorId`.`AuthorId` = `BookSystem_EditionAuthors`.`AuthorId`)
) AS `PrimaryAuthorId`
from `BookSystem_EditionAuthors`;
Now I can do:
SELECT * FROM BookSystem_EditionAuthorsWithPrimary WHERE EditionId=10;
BindingId, EditionId, AuthorId, Position, PrimaryAuthorId
10, 10, 10, 0, 7
Much nicer!
this next query is a great example
select
`BookSystem_BookList`.`BookId` AS `Id`,
`BookSystem_BookList`.`Title` AS `Name`,
`BookSystem_BookList`.`UserId` AS `UserId`,
`BookSystem_BookList`.`BookType` AS `Subtype`,
1 AS `IsBook`,0 AS `IsSeries`,
0 AS `IsAuthor`
from `BookSystem_BookList`
union
select
`BookSystem_SeriesList`.`SeriesId` AS `Id`,
`BookSystem_SeriesList`.`SeriesName` AS `Name`,
`BookSystem_SeriesList`.`UserId` AS `UserId`,
'' AS `Subtype`,
0 AS `IsBook`,
1 AS `IsSeries`,
0 AS `IsAuthor`
from `BookSystem_SeriesList`
union
select
`BookSystem_AuthorList`.`AuthorId` AS `Id`,
concat(
`BookSystem_AuthorList`.`AuthorSurname`,', ',`BookSystem_AuthorList`.`AuthorForename`,
ifnull(
(select concat(
' (AKA: ',
group_concat(
concat(
`BookSystem_AuthorList`.`AuthorSurname`,
', ',
`BookSystem_AuthorList`.`AuthorForename`
) separator '; '
),')'
) AS `AKA` from `BookSystem_AuthorList`
where
(`BookSystem_AuthorList`.`duplicateOf` = `Id`)
group by (`BookSystem_AuthorList`.`duplicateOf` = `Id`)
),'')) AS `Name`,
`BookSystem_AuthorList`.`UserId` AS `UserId`,
'' AS `SubType`,
0 AS `IsBook`,
0 AS `IsSeries`,
1 AS `IsAuthor`
from `BookSystem_AuthorList`
where (`BookSystem_AuthorList`.`duplicateOf` = 0) order by `Name`;
IS HUGE!
But now I can get all the things for UserId=1 easily:
mysql> SELECT * FROM BookSystem_Index WHERE UserId = 1;
+----+----------------------------------------+--------+-------------+--------+----------+----------+
| Id | Name | UserId | Subtype | IsBook | IsSeries | IsAuthor |
+----+----------------------------------------+--------+-------------+--------+----------+----------+
| 4 | A First Course in Calculus | 1 | Normal | 1 | 0 | 0 |
| 2 | A First Course in Real Analysis | 1 | Normal | 1 | 0 | 0 |
| 2 | Algebra | 1 | | 0 | 1 | 0 |
| 13 | Analysis II assignments | 1 | Assignments | 1 | 0 | 0 |
| 14 | Author Test | 1 | Normal | 1 | 0 | 0 |
| 8 | b, g | 1 | | 0 | 0 | 1 |
| 7 | b, g (AKA: t, lll; Teal, lll) | 1 | | 0 | 0 | 1 |
| 1 | Calculus of Several Variables | 1 | Normal | 1 | 0 | 0 |
| 4 | DuBois, Paul | 1 | | 0 | 0 | 1 |
| 1 | Lang, Serge (AKA: Lang, S. E. R. G. E) | 1 | | 0 | 0 | 1 |
| 5 | Linear Algebra | 1 | Normal | 1 | 0 | 0 |
| 3 | Morrey, C. B. | 1 | | 0 | 0 | 1 |
| 6 | MySQL | 1 | Normal | 1 | 0 | 0 |
| 7 | Principles of Mathematical Analysis | 1 | Normal | 1 | 0 | 0 |
| 2 | Protter, M. H. | 1 | | 0 | 0 | 1 |
| 5 | Rudin, Walter | 1 | | 0 | 0 | 1 |
| 10 | t | 1 | Normal | 1 | 0 | 0 |
| 3 | Test | 1 | | 0 | 1 | 0 |
| 12 | Test 1 | 1 | Normal | 1 | 0 | 0 |
| 11 | Test 4.4.2014 | 1 | Normal | 1 | 0 | 0 |
| 8 | Topology and Analysis | 1 | Normal | 1 | 0 | 0 |
| 3 | Undergraduate Algebra | 1 | Normal | 1 | 0 | 0 |
| 1 | Undergraduate Texts in Mathematics | 1 | | 0 | 1 | 0 |
| 9 | w | 1 | Normal | 1 | 0 | 0 |
+----+----------------------------------------+--------+-------------+--------+----------+----------+
24 rows in set (0.00 sec)
The optimiser sees the view properly, it wont generate the full view, it effectively substitutes the required selects.
(Taken from a testing DB, not production, hence weird names like "TESTING")
First, the room type selection needs to be framed correctly. The following join would probably work.
EDIT:
The query has been edited to return only properties with all three room types. It has also been joined with the plan table.
SELECT
COUNT(pl.day) AS Days,
p.property_ID AS Hotel_ID,
p.name AS Hotel_Name,
r.room_name AS Room_Name,
r.room_type_ID AS Room_ID,
r.max_adults as Max_Adults,
r.max_children as Max_Children
FROM property p
INNER JOIN room_type r
ON p.property_ID=r.property_ID
INNER JOIN plan pl
ON pl.room_type_ID=r.room_type_ID
AND (pl.day >= '2014-07-07' AND pl.day <= '2014-07-11')
WHERE EXISTS
(SELECT 1
FROM room_type r1
WHERE p.property_ID=r1.property_ID
AND r1.max_adults = 2 AND r1.max_children = 0)
AND EXISTS
(SELECT 1
FROM room_type r2
WHERE p2.property_ID=r2.property_ID
AND r2.max_adults = 4 AND r2.max_children = 2)
AND EXISTS
(SELECT 1
FROM room_type r3
WHERE P.PROPERTY_ID=R3.PROPERTY_ID
AND r3.max_adults = 2 AND r3.max_children = 1)
GROUP BY
p.property_ID,
p.name,
r.room_name,
r.room_type_ID,
r.max_adults,
r.max_children
HAVING
COUNT(pl.day) = 4;