I'm trying to populate a table in HTML using a PHP script, which runs an SQL query which returns the following results (example):
UserID | Name | Month | Value |
1 | Joe | Jan | 123.00 |
1 | Joe | Feb | 22.00 |
1 | Joe | March | 32.50 |
21 | Derek | Jan | 45.76 |
21 | Derek | March | 12.31 |
But when I want to populate the table in PHP I want the table to look like this:
Name | January | February | March |
Joe | 123.00 | 22.00 | 32.50 |
Derek | 45.76 | | 12.31 |
But when generating the table using a PHP while loop its doing the following, which I would expect, as it's just looping through each fetched row:
Name | January | February | March |
Joe | 123.00 | | |
Joe | 22.00 | | |
Joe | 32.500 | | |
Derek | 45.76 | | | ... etc`
As I said, I would expect it to behave this way, but is there anyway to get it to display in the 2nd example.
I've already worked out some logic to place the values in the correct location, but it's still on a separate row, so at the moment it looks like this, which isn't ideal.
Name | January | February | March |
Joe | 123.00 | | |
Joe | | 22.00 | |
Joe | | | 32.50 |
Derek | 45.76 | | |
I hope I'm not missing anything obvious but I've had a look at several solutions within SQL but just can't seem to get it to work.
Any help would be appreciated.
EDITED:
Apologies, my SQL query is taking data from two different tables, as I am comparing values, so my understanding was that I couldn't use the GROUP BY statement. Here is my SQL query:
SELECT dbo_tstUser.UserID, dbo_tstUser.Name, dbo_tstUser.Month, dbo_tstUser.Value, dbo_tstUserImport.Value FROM dbo_tstUser INNER JOIN dbo_tstUserImport ON dbo_tstUser.UserID = dbo_tstUserImport.UserID;
You can use aggregation to do the pivoting:
select UserId, name,
max(case when Month = 'Jan' then value end) as January,
max(case when Month = 'Feb' then value end) as February,
max(case when Month = 'March' then value end) as March
from your_table
group by UserId, name;
If you don't want the UserId in the result, you can use:
select name,
max(case when Month = 'Jan' then value end) as January,
max(case when Month = 'Feb' then value end) as February,
max(case when Month = 'March' then value end) as March
from your_table
group by UserId, name;
Note that I kept the UserId in the group by clause to keep different people with same name separate.
You need to add a GROUP BY name statement to your SQL:-
SELECT UserID, Name, Month, Value
FROM table
GROUP BY Name
Edit:-
SELECT dbo_tstUser.UserID, dbo_tstUser.Name, dbo_tstUser.Month, dbo_tstUser.Value, dbo_tstUserImport.Value
FROM dbo_tstUser
INNER JOIN dbo_tstUserImport ON dbo_tstUser.UserID = dbo_tstUserImport.UserID
GROUP BY dbo_tstUser.UserID;
Related
What I want to do is select everything from the database for a specific dancer group that by date and then look for the latest date for each dance and only show those.
See attached image of what results (big red arrows) I would want to return from the database. I this example the single jig was not danced on the 11th of Sept but I need the last time this was danced (10th) so I can then calculate the current level a dancer is at for all dances.
Try
SELECT t.*
FROM table1 t JOIN (
SELECT dancer_id, dance, MAX(date) date
FROM table1
GROUP BY dancer_id, dance
) q ON t.dancer_id = q.dancer_id
AND t.dance = q.dance
AND t.date = q.date
Here is a SQLFiddle
Sample Output:
| id | dancer_id | date | dance | level | placed | venue |
|-----|-----------|-----------------------------|------------|-------|--------|--------|
| 143 | 130 | September, 10 2016 00:00:00 | Single Jig | 1 | 4 | (null) |
| 146 | 130 | September, 11 2016 00:00:00 | Reel | 2 | 5 | (null) |
| 147 | 130 | September, 11 2016 00:00:00 | Light Jig | 2 | 4 | (null) |
| 148 | 130 | September, 11 2016 00:00:00 | Slip Jig | 2 | 4 | (null) |
I have following table.
---+---------------+-------------+--------+
id | department_id | created | amount |
---+---------------+-------------+--------+
1 | 0 | 2016-05-05 | 500 |
2 | 2 | 2016-05-05 | 100 |
3 | 1 | 2016-05-05 | 160 |
4 | 2 | 2016-05-05 | 260 |
5 | 1 | 2016-05-04 | 100 |
6 | 0 | 2016-05-04 | 150 |
7 | 1 | 2016-05-04 | 160 |
8 | 2 | 2016-05-04 | 160 |
---+---------------+-------------+--------+
I want to generate report to show how much collection is there on particular day in particular department & need to show like below table:
Date | 0 | 1 | 2 |
2016-05-05 | 500 | 160 | 360 |
2016-05-04 | 150 | 260 | 160 |
I am using cakephp. If I use GROUP BY created it works and if I use GROUP BY department_id it works. But I need both GROUP BY to generate the required report.
I also tried generating the from to date array using this. and then tried running GROUP BY created on each date using following CakePHP query:
//$ar = date array
foreach ($ar as $k) {
$this->Transaction->find('all',array('conditions'=>array('Transaction.created'=>$k),'fields'=>array('SUM(Transaction.amount) AS s','Transaction.department_id'),'group'=>array('Transaction.department_id')));
}
But it seems like slow solution to me if date range is long. Is there any best way to do it in single query.
Please help me sorting this problem.
Variations of this question are asked and answered endlessly. A typical solution goes something like this...
SELECT created date
, SUM(CASE WHEN department_id = 0 THEN amount END) dept0
, SUM(CASE WHEN department_id = 1 THEN amount END) dept1
, SUM(CASE WHEN department_id = 2 THEN amount END) dept2
FROM my_table
GROUP
BY date;
simple group by two columns department_id and created - and sum(amount) function to calculate total for each group.
$report_rows = $this->{{Model_NAME}}->find('all', array(
'fields' => array('department_id', 'created', 'sum(amount)'),
'group' => 'department_id, created'
));
and then you can show this report on view
$dates = Set::extract('/{{Model_NAME}}/created', $report_rows);
$departments = Set::extract('/{{Model_NAME}}/department_id', $report_rows);
Now you can make dates in columns and departments as row title. Hope this help, it's not complied, may contain some bugs but logic will work well.
I need to get data back which isn't in another table, for a certain date.
+------+--------+---------------------+---------------------+
| calID| jobID | startDate | endDate |
+-- ---+--------+---------------------+---------------------+
| 1 | 2 | 2016-05-13 00:00:00 | 2016-05-13 00:00:00 |
| 2 | 3 | 2016-05-14 00:00:00 | 2016-05-14 00:00:00 |
| 3 | 1 | 2016-05-15 00:00:00 | 2016-05-15 00:00:00 |
+------+--------+---------------------+---------------------+
This is the job_calendar. I need staff which are not associated with a Job for a given date.
+------+--------+---------+
| id | calID | staffID |
+-- ---+--------+---------+
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 1 | 3 |
+------+--------+---------+
This is the job_staff table for the staff member and the jobs.
+---------+-----------+----------+
| staffID | firstName | lastName |
+---------+-----------+----------+
| 1 | John | Smith |
| 2 | Max | Power |
| 3 | Jane | Doe |
+---------+-----------+----------+
And finally the resource_staff table. This stores all user information. I have been playing around with some queries before and although I think I'm close I need a little help.
SELECT
*
FROM
resource_staff
LEFT JOIN
job_staff
ON
resource_staff.staffID = job_staff.staffID
LEFT JOIN
job_calendar
ON
job_staff.calID = job_calendar.calID
WHERE
job_staff.staffID IS NULL
AND
job_calendar.startDate = "2016-05-13 00:00:00"
Any help would be greatly appreciated, sorry if I've been a nuisance or otherwise.
EDIT:
I expect to get all result which have not been assigned to a job for a particular date which the user selects.
try this
SELECT
*
FROM
resource_staff
where
staffID NOT IN (select jc.staffID from job_calendar as jc
JOIN job_staff as js ON js.calID=jc.calID
WHERE "2016-05-13 00:00:00" BETWEEN jc.startDate and jc.endDate )
try this:
SELECT
*
FROM
ressource_staff rs
LEFT JOIN (
SELECT
rs2.staffID
FROM
ressource_staff rs2
LEFT JOIN
job_staff js
ON
rs2.staffID=js.staffID
LEFT JOIN
job_calendar jc
ON
js.calID=jc.calID
WHERE
jc.startDate < "2016-05-13 00:00:00"
AND
jc.endDate > "2016-05-00:00:00") sq
ON
rs.staffID=sq.staffID
WHERE
sq.staffID IS NULL
just replace the date string with your date and it will answer a record that is not used at that date.
I guess this is a recurring question but I haven't found the right topic to point me in the right direction.
I have chat table which pretty much looks like:
+---------+----------------+---------------------+-----------------+
| id(int) | author(string) | date(datetime) | message(string) |
+---------+----------------+---------------------+-----------------+
| 1 | John | 2016-01-01 17:18:00 | I |
| 2 | Mary | 2016-01-01 14:22:00 | Just |
| 3 | John | 2016-01-01 09:02:00 | Want |
| 4 | John | 2016-01-02 17:18:00 | To |
| 5 | Mary | 2016-01-03 18:26:00 | Say |
| 6 | John | 2016-01-03 10:42:00 | Hello |
+---------+----------------+---------------------+-----------------+
What I would like to get:
+------------+------+------+
| day | Mary | John |
+------------+------+------+
| 2016-01-01 | 1 | 2 |
| 2016-01-02 | 0 | 1 |
| 2016-01-03 | 1 | 1 |
+------------+------+------+
Am I obligated to do a subquery in the COUNT statement ?
So far I came up with:
SELECT DATE(date) as day,
(SELECT COUNT(id) FROM chat WHERE author = 'Mary') AS 'Mary'
(SELECT COUNT(id) FROM chat WHERE author = 'John') AS 'John'
FROM chat
GROUP BY day
ORDER BY day ASC
But this is giving me the total message count per author at each row:
+------------+------+------+
| day | Mary | John |
+------------+------+------+
| 2016-01-01 | 2 | 4 |
| 2016-01-02 | 2 | 4 |
| 2016-01-03 | 2 | 4 |
+------------+------+------+
Just use conditional aggregation:
SELECT DATE(date) as day,
SUM(author = 'Mary') AS Mary,
SUM(author = 'John') AS John
FROM chat
GROUP BY day
ORDER BY day ASC
I might be wrong, but it looks for me like you search for a solution, where you have a small group of the same users? Even if it is true, I would change the logic in a way to avoid generating the query and having problems for larger amounts of users. It might not be the perfect solution for your current problem, but might help to save some time in the future. So I would use a different pattern.
As query, I would use something like this:
SELECT DATE(`date`) AS day,
`author`,
COUNT(`id`) AS messagecount
FROM `chat`
GROUP BY `day`, `author`
ORDER BY `day` ASC
With this you would get a result like this:
+------------+--------+--------------+
| day | author | messagecount |
+------------+--------+--------------+
| 2016-01-01 | Mary | 2 |
| 2016-01-01 | John | 4 |
| 2016-01-02 | Mary | 2 |
| 2016-01-02 | John | 4 |
+------------+--------+--------------+
After that, you can group the result in PHP to have the desired effect, for example generate an array like this using the date as key:
array(
'2016-01-01' => array(
'day' => '2016-01-01',
'Mary' => 2,
'John' => 4
),
'2016-01-02' => array(
'day' => '2016-01-02',
'Mary' => 2,
'John' => 4
),
)
I want to make a mysql union search. My purpose is: my total results back must be 10.
if search results are more than 10, the returned data come all from search result.
if search results are less than 10, first few returned data come from search result, and then fetch the remaining results from database order by date.
To make it clearer: if a client searches "today", my database only returns 7 results which contain "today", then add another 3 results from my database ORDER BY date. So that the total results are 10 items.
Another purpose: another 3 results are not repetitions from the 7 results which match the search. I think UNION or UNION DISTINCT can do that job, am I right?
So, how do I do a query like this?
PS: my code will fix the result order, but I need first select is always behind the second select
(SELECT * FROM table WHERE title like %$searchword% limit 0,10 ORDER BY date)
UNION
(SELECT * FROM table limit 0,10 ORDER BY date)
limit 0,10 ORDER BY date
If you always want 10 results:
SELECT
IF(m.id,1,0) AS has_match,
t.*
FROM
`table` t
LEFT JOIN `table` m ON m.id = t.id AND m.title LIKE '%$searchword%'
GROUP BY t.id
ORDER BY has_match DESC, date
LIMIT 10
Tested:
mysql> select * from `table`;
+----+------------------------+---------------------+
| id | title | date |
+----+------------------------+---------------------+
| 1 | test 1 | 2011-11-06 10:27:08 |
| 2 | test 2 match | 2011-11-06 10:27:14 |
| 3 | 3 match this too | 2011-11-06 10:27:23 |
| 4 | title does NOT | 2011-11-06 10:27:44 |
| 5 | Another matching title | 2011-11-06 10:27:55 |
| 6 | this does not either | 2011-11-06 10:29:22 |
| 7 | Do not put this first | 2011-11-06 10:29:37 |
| 8 | Is this number 8? | 2011-11-06 10:29:57 |
| 9 | The 9th is a match | 2011-11-06 10:30:07 |
| 10 | 10th does not | 2011-11-06 10:30:20 |
| 11 | 11th IS a match too! | 2011-11-06 10:30:37 |
| 12 | 12th gets ignored? | 2011-11-06 10:30:49 |
+----+------------------------+---------------------+
12 rows in set (0.00 sec)
mysql> SELECT IF(m.id,1,0) AS has_match, t.* FROM `table` t LEFT JOIN `table` m ON m.id = t.id AND m.title LIKE '%match%' GROUP BY t.id ORDER BY has_match DESC, date LIMIT 10;
+-----------+----+------------------------+---------------------+
| has_match | id | title | date |
+-----------+----+------------------------+---------------------+
| 1 | 2 | test 2 match | 2011-11-06 10:27:14 |
| 1 | 3 | 3 match this too | 2011-11-06 10:27:23 |
| 1 | 5 | Another matching title | 2011-11-06 10:27:55 |
| 1 | 9 | The 9th is a match | 2011-11-06 10:30:07 |
| 1 | 11 | 11th IS a match too! | 2011-11-06 10:30:37 |
| 0 | 1 | test 1 | 2011-11-06 10:27:08 |
| 0 | 4 | title does NOT | 2011-11-06 10:27:44 |
| 0 | 6 | this does not either | 2011-11-06 10:29:22 |
| 0 | 7 | Do not put this first | 2011-11-06 10:29:37 |
| 0 | 8 | Is this number 8? | 2011-11-06 10:29:57 |
+-----------+----+------------------------+---------------------+
10 rows in set (0.00 sec)