I'm trying to make this query work:
SELECT
IFNULL(SUM(days), 0) AS days
FROM
`table_days`
WHERE task = 1
GROUP BY task
UNION
ALL
SELECT
IFNULL(SUM(total), 0) AS total
FROM
`table_total`
WHERE task = 1
GROUP BY task ;
I have two tables :
1. table_days
id task days
==========================
1 1 3.00
2 1 2.00
2. table_total
id task total
==========================
1 3 0.00
The query above partially works, the result is:
stdClass Object
(
[days] => 5.00
)
but I would like to get the result from second table even if there are no records found. Something like
stdClass Object
(
[days] => 5.00
[total] => 0.00
)
Try This query
SELECT
IFNULL(SUM(days), 0) AS days
FROM
`table_days`
WHERE task = 1
GROUP BY task
UNION
ALL
SELECT
SUM(case when task = 1 then IFNULL(total,0) else 0 end) AS total
FROM
`table_total`
GROUP BY task ;
This code will work for you (example at sqlfiddle):
SELECT
IFNULL(SUM(days), 0) AS value, 'days' as name
FROM
`table_days`
WHERE task = 1
GROUP BY task
UNION ALL
(SELECT CASE WHEN u.value = -1 then 0 else value end as value, 'total' as name
FROM
(
SELECT
IFNULL(SUM(total), 0) AS value
FROM
`table_total`
WHERE task = 1
GROUP BY task
UNION
SELECT -1 as value
) u
);
And it will return:
+-------+-------+
| value | name |
+-------+-------+
| 5 | days |
| 0 | total |
+-------+-------+
The conundrum with your current approach is that the second half of your UNION query doesn't "know" anything about the tasks which appear in the first half.
However, joining the two tables would allow you to leverage the relationship between the two tables. I think a better way of doing this would be to LEFT JOIN the table_days table with table_total, thereby retaining all tasks:
SELECT t1.task, t1.sum_days, t2.sum_total
FROM
(
SELECT task, IFNULL(SUM(days), 0) AS sum_days
FROM `table_days`
GROUP BY task
) t1
LEFT JOIN
(
SELECT task, IFNULL(SUM(total), 0) AS sum_total
FROM `table_total`
GROUP BY task
) t2
ON t1.task = t2.task
WHERE t1.task = 1
If you want to include all tasks from the table_days table, you can remove the WHERE clause.
Related
I have read carefuly through all the posts on here about this topic, but my question has one extra catch, where I need to get available beds of each room because its a hostel and some rooms are shared.
Now I have tried doing this in PHP, but then realized I haven't even taken in account the date range. So now I am thinking since I already have a query that retrieves all reservations occurring within a given date range and then compare the retrieved rooms and their beds with the room table and show only rooms and their beds that are not occupied. But I can't figure out how to work around the beds because they are not an entity, only a count of total beds in each room. But then a reservation says for which bed in the room that reservation is made..
Here are my tables
Rooms
Reservations
Now the query that I use to retrieve all reserved rooms and their beds is
SELECT rooms_id, bed
FROM reservations
WHERE `to` > '2016-02-18' AND `from` < '2016-02-24'
The first date if my input variable $from and the second date is input variable $to and it allows to retrieve not only rooms that have a reservation that starts within $from and $to but also all reservations that started before the date range and end inside, started inside and end after the date range and finally reservations that started before and end after the date range. So this exact query above would return the following table
which I can then visualize in my app like this
But this is where I get stuck. I have no idea how to match the data I have in order to find all available rooms BUT ALSO THE BEDS.
The desired table AVAILABLE ROOMS for the date range from '2016-02-18' and to '2016-02-24' should look like this:
|||||||||||||||||||||||||||||||||
|| rooms_id || bed_number ||
||||||||||||||||||||||||||||
|| 1 || 1 ||
----------------------------
|| 1 || 2 ||
----------------------------
|| 2 || 5 ||
----------------------------
|| 2 || 6 ||
----------------------------
|| 2 || 7 ||
----------------------------
|| 2 || 8 ||
----------------------------
You can see this in the picture where I show how it looks in my app. The only rooms and beds that are available between the two dates are the Luxury Room and its bed number 1 and bed number 2 and the Dorm rooms beds 5,6,7,8 because 1-4 have a reservation occurring on at least on one of the desired dates
The only idea I had was using NOT IN, but that only works if I didn't care about the beds and also the output is of here it is
SELECT *
FROM `rooms`
WHERE `id` NOT IN
(SELECT rooms_id FROM reservations WHERE `to` > '2016-02-18' AND `from` < '2016-02-24')
instead of what I "sketched" above
I'd appreciate any tips and ideas on how approach this.
Part of me worries that this will all come down to me not treating the beds as entities and having to do that, even though as I will never be storing any sort of information on the beds such as their color, position, quality, price, etc...
REPLY TO #Paul-Spiegel
That is amazing, but is there any way to get the free beds as numbers as well instead of total number. Because then when the person makes a reservation I have to assign it to on of the beds. So if the result could be
| room_id | title | beds_total | available_bed_nrs |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 1 | luxury room | 2 | 1, 2 |
| 2 | dorm room | 8 | 5, 6, 7, 8 |
instead of
You can get a number of free rooms (and the reserved room numbers) with this query:
set #from := '2016-02-18';
set #to := '2016-02-24';
set #beds := 1;
SELECT rm.id, rm.title, rm.beds,
rm.beds - IFNULL(rv.num_reserved_beds, 0) AS num_free_beds,
rv.reserved_bed_nrs
FROM rooms rm
LEFT JOIN (
SELECT rv.rooms_id,
COUNT(1) as num_reserved_beds,
GROUP_CONCAT(rv.bed) as reserved_bed_nrs
FROM reservations rv
WHERE rv.from < #to
AND rv.to > #from
GROUP BY rv.rooms_id
) rv ON rv.rooms_id = rm.id
HAVING num_free_beds >= #beds
You can now parse reserved_bed_nrs, loop over all beds per room and pick the beds that are not in reserved_bed_nrs.
Explaination:
Get all beds reserved within date range (excluding):
SELECT *
FROM reservations r
WHERE r.from < #to
AND r.to > #from;
Group by room, count the number of reserved rooms and store all numbers of reserved rooms in one string field:
SELECT rv.rooms_id,
COUNT(1) as num_reserved_beds,
GROUP_CONCAT(rv.bed) as reserved_bed_nrs
FROM reservations rv
WHERE rv.from < #to
AND rv.to > #from
GROUP BY rv.rooms_id
Join (LEFT JOIN) rooms with the given result calculate the number of free beds and compare it with the number of beds you want to book.
Update How to get free (not reserved) beds:
If you don't have a table with all existing beds, you will need some kind of sequece numbers. Assuming a room can have a maximum of 100 beds you can create a sequence table with 100 numbers:
CREATE TABLE `sequence` (
`nr` TINYINT(3) UNSIGNED NOT NULL,
PRIMARY KEY (`nr`)
) select d1.d*10 + d0.d + 1 as nr from
(select 0 d union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) d0,
(select 0 d union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) d1
Now it's possible to list all existing beds by cross joining the tables rooms and sequence:
SELECT *
FROM rooms rm
CROSS JOIN sequence seq
WHERE seq.nr <= rm.beds
To list all not resverd beds you can combine it with a query for reserved beds (select all beds that are not reserved within the booking date range):
SELECT *
FROM rooms rm
CROSS JOIN sequence seq
WHERE seq.nr <= rm.beds
AND (rm.id, seq.nr) NOT IN (
SELECT rv.rooms_id, rv.bed
FROM reservations rv
WHERE rv.from < '2016-02-24'
AND rv.to > '2016-02-18'
)
This can also be done with NOT EXISTS or excluding LEFT JOIN.
You also can skip the creation of the sequence table use the creation code as subselect:
SELECT *
FROM rooms rm
CROSS JOIN (
select d1.d*10 + d0.d + 1 as nr
from
(select 0 d union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) d0,
(select 0 d union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) d1
) seq
WHERE seq.nr <= rm.beds
AND (rm.id, seq.nr) NOT IN (
SELECT rv.rooms_id, rv.bed
FROM reservations rv
WHERE rv.from < '2016-02-24'
AND rv.to > '2016-02-18'
)
http://sqlfiddle.com/#!9/a0d61/5
I have a simple table called users with the following data:
id | hops
1 | 3
2 | 1
3 | 5
4 | 2
5 | 4
6 | 5
I want to select the number of hops of any given id that I specify and also select the next and previous ids according to the number of hops sorted from highest to lowest.
To explain more I use the following query:
SELECT * FROM test WHERE id = 1
OR (id > 1 AND hops >= (SELECT hops FROM test WHERE id= 1) )
OR (id < 1 AND hops <= (SELECT hops FROM test WHERE id= 1) )
LIMIT 3
So in the above query I tried to get id=1, next id with the same or higher number of hops, and the previous id with the same or lower number of hops.
This is the result i get:
id | hops
1 | 3
3 | 5
5 | 4
As you can see it selected id=1 and two higher ids although I want only one higher id and one lower id. So, in this case the result should be like this instead:
id | hops
1 | 3
3 | 5
As there is no lower id than 1, so nothing lower to fit the criteria and selects only 1 higher id. The wrong result is because of using LIMIT 3 but I can't use a LIMIT for each condition. So don't know how to approach this at all.
Have another question, would using the sub-query
"SELECT hops FROM test WHERE id= 1"
slow down the server on a large scale?? I heard that it's not preferable to use sub-queries but have no other way to get this number except using a separate query.
Thanks
here you go, change the order by ID according to your liking...you didn't say if you wanted the closest number of hops or the closest ID, just one greater or lower
SELECT * FROM test
WHERE id IN (1,(
SELECT id FROM test WHERE id > 1 AND hops >= (
SELECT hops FROM test WHERE id = 1
) ORDER BY id LIMIT 1
), (
SELECT id FROM test WHERE id < 1 AND hops <= (
SELECT hops FROM test WHERE id = 1
) ORDER BY id DESC LIMIT 1
))
If I understand your question correctly, I believe the following will work.
-- Previous Rec
SELECT t2.*
FROM test as t1
JOIN test as t2 ON t2.hop <= t1.id
WHERE t1.id = 1
ORDER BY t2.id DESC
LIMIT 1
UNION ALL
-- Current Rec
SELECT *
FROM test as t
WHERE id = 1
UNION ALL
-- Following Rec
SELECT t2.*
FROM test as t1
JOIN test as t2 ON t2.id >= t1.hop
WHERE t1.id = 1
ORDER BY t2.id ASC
LIMIT 1
I have this table:
This selection is is duplicated many times for different var_lines (which pretty much work as one row of data, or respondent for a survey) and set_codes (different survey codes).
With this query:
SELECT
*, COUNT(*) AS total
FROM
`data`
WHERE
`var_name` = 'GND.NEWS.INT'
AND(
`set_code` = 'BAN11A-GND'
OR `set_code` = 'BAN09A-GND'
OR `set_code` = 'ALG11A-GND'
)
AND `country_id` = '5'
GROUP BY
`data_content`,
`set_code`
ORDER BY
`set_code`,
`data_content`
The query basically counts the number of answers for a specific question. Then groups them survey (set_code).
What I need is for each of the grouped data_content answers for GND.NEWS.INT to also show the SUM of all the corresponding GND_WT with the same var_line.
For example if I had this:
data_id data_content var_name var_line
1 2 GND.NEW.INT 1
2 1.4 GND_WT 1
3 2 GND.NEW.INT 2
4 1.6 GND_WT 2
5 3 GND.NEW.INT 3
6 0.6 GND_WT 3
I would get something like this:
data_id data_content var_name var_line total weight
1 2 GND.NEW.INT 1 2 3
5 3 GND.NEW.INT 3 1 0.6
Thanks for any help.
Your requirements are not exactly clear, but I think the following gives you what you want:
select d1.data_id,
d1.data_content,
d1.var_name,
d1.var_line,
t.total,
w.weight
from data d1
inner join
(
select data_content,
count(data_content) Total
from data
group by data_content
) t
on d1.data_content = t.data_content
inner join
(
select var_line,
sum(case when var_name = 'GND_WT' then data_content end) weight
from data
group by var_line
) w
on d1.var_line = w.var_line
where d1.var_name = 'GND.NEW.INT'
See SQL Fiddle with Demo
This Query can be suitable for your specific example:
select st.data_id,
st.data_content,
st.var_name,
st.var_line,
count(st.data_id) as total,
sum(st1.data_content) as weight
from data st
left join data st1 on st1.var_name = 'GND_WT' AND st1.var_line=st.var_line
where st.var_name='GND.NEW.INT'
group by st.data_content
Regards,
Luis.
I need to get data from multiple tables from a database and I need to use 1 query. The trouble I am having is that I need to count how many tasks there are for each project and how many tasks are finished.
I got these table:
projects:
id name start_date end_date project_leader finished
1 project_1 2012-08-01 00:00:00 2012-29-01 00:00:00 2 0
users
id username password email status
1 user_1 pass_1 email_1 1
2 user_2 pass_2 email_2 1
user_has_project
userid projectId
1 1
tasks
id project description end_date user finished
1 1 test description 1 2012-29-01 00:00:00 1 1
2 1 test description 2 2012-29-01 00:00:00 1 0
So what I need to do, is make a query that should give me this result:
Wanted Result:
project_id project_name start_date end_date project_leader finished tasks finished_tasks
1 project_1 2012-08-01 00:00:00 2012-29-01 00:00:00 user_2 0 2 1
I got it to work until the part where I need to count the amount of tasks that are finished. I got this query so far, but it doesn't count the finished tasks yet. How can I do this?
Query:
SELECT projects.id,
projects.name,
projects.start_date,
projects.end_date,
projects.finished,
users.username AS project_leader,
COUNT(tasks.id) AS tasks
FROM projects
LEFT JOIN tasks ON (tasks.project = projects.id)
JOIN user_has_project ON (user_has_project.projectId = projects.id)
JOIN users ON (projects.project_leader = users.id)
WHERE user_has_project.userId = 1
GROUP BY projects.id
I'm jumping the gun a little bit here (I'll continue contemplating in case this gut-reaction is wrong), but the trick might be to use a "null if false" evaluation inside the count() function, which is documented to only count non-null values:
SELECT projects.id,
projects.name,
projects.start_date,
projects.end_date,
projects.finished,
users.username AS project_leader,
COUNT(tasks.id) AS tasks,
COUNT(NULLIF(tasks.finished, 0)) as finished_tasks
Here is a simplified version of my sql table of 2 months (ORDERED BY DATE):
player_id |
date |
score
1 2011-05-25
1200
2 2011-05-25
3400
3 2011-05-26
3200
4 2011-05-26
4400
1 2011-05-28
1000
2 2011-05-28
2000
3 2011-05-29
3000
4 2011-05-29
4000
1 2011-06-24
1300
2 2011-06-24
2500
3 2011-06-24
5000
4 2011-06-24
3000
Basically, I want a query that shows the last score of all players in a specific month/specific year.
Example:
If I want the final scores of all players in the month 05, te result
would be:
1 2011-05-28 1000
2 2011-05-28 2000
3 2011-05-29 3000
4 2011-05-29 4000
My sql query so far:
SELECT m1.* FROM table m1
LEFT JOIN table m2 ON (m1.player_id = m2.player_id AND m1.date < m2.date)
WHERE m2.date IS NULL
AND month(m1.date) = 05
AND year(m1.date) = 2011
ORDER BY score DESC);
This doesn't seem to show all players, only players that didn't play in the months after 05. Where do I add the date select?
**EDIT
John Nestoriak's answer bellow did the trick for me :)
I think he's referring to the technique shown here: Retrieving the last record in each group
With the additional constraint of he doesn't want the last record but the last record in a given month.
Oddly enough you have to give that additional constraint twice, once in the join condition and again to filter the results. This should do it for you.
SELECT m1.* FROM table m1
LEFT JOIN table m2 ON
(m1.player_id = m2.player_id AND m1.date < m2.date
AND m2.date < '2011-06-01')
WHERE m2.date IS NULL AND month(m1.date) = 5 AND year(m1.date) = 2011
Assuming that the (player_id, date) combination in Unique:
SELECT
t.*
FROM
TableX AS t
JOIN
( SELECT
player_id
, MAX(date) AS maxDate
FROM
TableX
WHERE
date BETWEEN '2011-05-01'
AND LAST_DAY('2011-05-01')
GROUP BY
player_id
) AS tg
ON
(tg.player_id, tg.maxDate) = (t.player_id, t.date)
ORDER BY
t.score DESC