Get sum of value with inner join mysql - mysql

I have a table for terminal
Id status
1 Online
2 Offline
3 Offline
and I have a separate table where I can find the total hours/date of the up and downtime.
total_time
Id up down
1 10 14
2 20 4
3 15 9
1 5 19
2 4 20
3 10 14
I want to display the terminal id, status and the TOTAL up and downtime(1 = 15(up), 2 = 24(up), 3 = 25(up). I'm using inner join and I have no idea how i will be able to get the sum of the up and downtime..
SELECT terminal.Id, terminal.status, total_time.Id, SUM(total_time.up),SUM(total_time.down)
FROM terminal
INNER JOIN total_time
ON terminal.Id = total_time.Id
WHERE terminal.Id = total_time.Id

Something like this should do the trick. I am interpreting your question as asking for only the sum of the time of the current status. If this is not what you want (and maybe you want the sum of both times), please let me know.
SELECT t.id, t.status, IF(t.status = 'Online', ttlTime.upTime, ttlTime.downTime) as totalTime
FROM terminal t
JOIN
(SELECT tt.id, SUM(tt.up) as upTime, SUM(tt.down) AS downTime
FROM total_time tt
GROUP BY tt.id) ttlTime ON t.id = ttlTime.id
See the SQLFiddle

You get "inner sums" by using the GROUP BY clause.
Try:
SELECT terminal.Id, SUM(total_time.up), SUM(total_time.down)
FROM terminal
INNER JOIN total_time
ON terminal.Id = total_time.Id
GROUP BY terminal.Id

SELECT DISTINCT terminal.Id, terminal.status, total_time.Id, SUM(total_time.up),SUM(total_time.down)
FROM terminal
INNER JOIN total_time
ON terminal.Id = total_time.Id
Should do it. The DISTINCT clause acts as a filter to remove duplicate records from a result set.

Related

Displaying values that occur consecutively

I'm trying to display a list of all Directors who have directed 2 years consecutively.
Given the following data:
Pantomime table:
Year titleID DirectorID
2000 1 1
2001 2 7
2002 3 7
2003 4 8
2004 5 9
2005 6 9
This is the desired outcome:
DirectorID
7
9
This is the query I have tried so far but was unable to get the desired results.
SELECT directorID
FROM pantomime
where directorID = directorID+1
GROUP BY directorID
One method uses exists:
select distinct p.directorId
from pantomine p
where exists (select 1
from pantomine p2
where p2.directorId = p.directorId and p2.year = p.year + 1
);
There are other fun variants on this idea, such as using in:
select distinct p.directorId
from pantomine p
where p.year in (select p2.year + 1
from pantomine p2
where p2.directorId = p.directorId
);
And here is a totally arcane method that doesn't use join-like mechanisms at all (just aggregation):
select distinct directorId
from ((select directorId, year from pantomine)
union all
(select directorId, year + 1 from pantomine)
) p
group by directorId, year
having count(*) = 2;
This is also one of those really, really rare cases of using select distinct with group by.
you can use join to see which entries has the next year's value, and then with distinct get the relevant id's:
select distinct a.directorID
from Pantomime as a
inner join Pantomime as b on a.year = b.year-1
and a.directorID = b.directorID;
since I'm using inner join, we'll get records from a only if they exist in b- meaning if year-1 appears in your table for this directorId
Try this, no joins or sub-queries, just a simple grouping:
SELECT directorID
FROM pantomime
GROUP BY directorID
HAVING COUNT(*) = 2
AND MAX(Year) = MIN(Year) + 1
Here is a fiddle.

Get product total sales per moth, with 0 in the gaps

I have been stuck in a recent problem with a SQL Query. What I'm trying to archieve is to get each product in the store and show how many of them has been sold each month. However, sometimes there are some months where these products were not sold, which means they won't be displayed.
For instance, this is the result I'm getting right now
Article Month Sold
CN140027 6 312
CN140027 7 293
CN140027 12 122
CN140186 1 10
CN140186 4 2
While I want to get something more like this
Article Month Sold
CN140027 6 312
CN140027 7 293
CN140027 8 0
CN140027 9 0
CN140027 10 0
CN140027 11 0
CN140027 12 122
CN140186 1 10
CN140186 2 0
CN140186 3 0
CN140186 4 2
And here is the query I'm using at the moment
SELECT k.artikelnr, Months.datefield as `Months`, IFNULL(SUM(k.menge),0) as `Quantity`
FROM store_shop_korb as k LEFT OUTER JOIN office_calendar AS Months
ON Months.datefield = month(k.date_insert)
WHERE k.date_insert BETWEEN "2014-12-01" AND "2015-12-31"
group by k.artikelnr, Months.datefield
What am I missing? Or what am I doing wrong? Any help is really appreciated.
Thanks in advance.
EDIT:
Additional information:
office_calendar is the calendar table. It only contains the months as registry, from 1 to 12.
Additionally, I'm taking the article/product ID from a table called 'store_shop_korb', which contains all the lines of a made order (so it contains the article ID, its price, the quantity for each order..)
This works for me:
SELECT k.artikelnr, c.datefield AS `Month`, COALESCE(s.Quantity, 0) AS Sold
FROM (
SELECT artikelnr
FROM store_shop_korb
GROUP BY artikelnr
) k
JOIN office_calendar c
LEFT JOIN (
SELECT artikelnr, MONTH(date_insert) AS monthfield, SUM(menge) AS Quantity
FROM store_shop_korb
GROUP BY artikelnr, MONTH(date_insert)
) s ON k.artikelnr = s.artikelnr AND c.datefield = s.monthfield
ORDER BY k.artikelnr, c.datefield
If you have a table of articles, you can use it in the place of subquery k. I'm basically normalizing on the fly.
Explanation:
There's basically 3 sets of data that get joined. The first is a distinct set of articles (k), the second is a distinct set of months (c). These two are joined without restriction, meaning you get the cartesian product (every article x every month). This result is then left-joined to the sales per month (s) so that we don't lose 0 entries.
Add another where condition , i think it will solve your problem
SELECT k.artikelnr, Months.datefield as `Months`, IFNULL(SUM(k.menge),0) as `Quantity`
FROM store_shop_korb as k LEFT OUTER JOIN office_calendar AS Months
ON Months.datefield = month(k.date_insert)
WHERE IFNULL(SUM(k.menge),0)>0 AND k.date_insert BETWEEN "2014-12-01" AND "2015-12-31"
group by k.artikelnr, Months.datefield
I have tried this in MSAccess and it seems to work OK
SELECT PRODUCT, CALENDAR.MONTH, A
FROM CALENDAR LEFT JOIN (
SELECT PRODUCT, MONTH(SALEDTE) AS M, SUM(SALEAMOUNT) AS A
FROM SALES
WHERE SALEDTE BETWEEN #1/1/2015# AND #12/31/2015#
GROUP BY PRODUCT, MONTH(SALEDTE) ) AS X
ON X.M = CALENDAR.MONTH
If you already have a calender table then use this.
SELECT B.Article,
A.Month,
COALESCE(c.Sold, 0)
FROM (SELECT DISTINCT Months.datefield --Considering this as months feild
FROM office_calendar AS Months) A
CROSS JOIN (SELECT DISTINCT article
FROM Yourtable) B
LEFT OUTER JOIN Yourtable C
ON a.month = c.Month
AND b.Article = c.Article
Else you need a months table. Try this.
SELECT *
FROM (SELECT 1 AS month UNION
SELECT 2 UNION
SELECT 3 UNION
SELECT 4 UNION
SELECT 5 UNION
SELECT 6 UNION
SELECT 7 UNION
SELECT 8 UNION
SELECT 9 UNION
SELECT 10 UNION
SELECT 11 UNION
SELECT 12) A
CROSS JOIN (SELECT DISTINCT article
FROM Yourtable) B
LEFT OUTER JOIN Yourtable C
ON a.month = c.Month
AND b.Article = c.Article

MySQL select values from Multiple Tables dependent on latest value in one

I have the following three tables to look after support tickets in a small web application, but I need some help getting the data I need.
Table 1 (ticket):
user_ID site_ID support_ID timestamp priority title
12 25 3 2014-09-26 14:09:25 0 A Test Row
12 26 4 2014-09-27 09:41:18 0 A 2nd Test Row
Table 2 (ticket_reply):
reply_ID support_ID user_ID support_reply reply_timestamp
3 3 12 some really boring text 2014-09-26 14:09:25
4 3 25 some really boring reply 2014-09-26 15:35:18
5 4 12 some really boring text 2014-09-27 09:41:18
Table 3 (ticket_status):
ticket_status_ID support_ID status_ID status_timestamp
3 3 40 2014-09-26 14:09:25
4 3 41 2014-09-26 15:35:18
5 4 40 2014-09-27 09:41:18
The 1st table holds the key ticket information, the 2nd, any replies made to the corresponding ticket, and the third tracks the change in status (statuses are held in another table, but don't need anything from there).
What I need to do is get the number of tickets where the latest status is == 40, and if this is greater than 0, get the latest reply along with the data from the first table.
I've tried multiple ways of doing this, but I am stuck. Don't really want to paste them here as they will likely confuse people, and I doubt they are even close.
This one was rather tricky, however here is a working solution for you.
This query will get the most recent support_reply value for all tickets where the most recent status_ID is 40.
SELECT
ticket_status_ID,
support_ID,
status_ID,
status_timestamp,
reply_ID,
support_reply,
reply_timestamp,
`timestamp` ticket_timestamp,
`priority` ticket_priority,
title
FROM (
SELECT * FROM (
SELECT * FROM (
SELECT
ticket_status.ticket_status_ID,
ticket_status.support_ID,
ticket_status.status_ID,
ticket_status.status_timestamp,
ts1.reply_ID,
ts1.user_ID,
ts1.support_reply,
ts1.reply_timestamp
FROM
ticket_status
INNER JOIN (SELECT * FROM ticket_reply ORDER BY reply_timestamp DESC) ts1 ON ts1.support_ID = ticket_status.support_ID
GROUP BY support_ID, status_ID
ORDER BY status_timestamp DESC
) ts2
GROUP BY ts2.support_ID
) ts3
INNER JOIN (SELECT support_ID as `ticket_support_ID`, site_ID, `timestamp`, priority, title FROM ticket) ts4 ON ts4.ticket_support_ID = ts3.support_ID
WHERE ts3.status_ID = 40
) ts5
From the example given, it looks that all timestamp are equivalent, so a query like this should be enough:
SELECT
ticket.*,
ticket_reply.*
FROM
(SELECT support_ID, MAX(status_timestamp) as max_timestamp
FROM ticket_status
GROUP BY support_ID) m
INNER JOIN ticket
ON m.support_ID=ticket.support_ID
AND m.max_timestamp=ticket.`timestamp`
INNER JOIN ticket_reply
ON m.support_ID=ticket_reply.support_ID
AND m.max_timestamp=ticket_reply.reply_timestamp
INNER JOIN ticket_status
ON m.support_ID=ticket_status.support_ID
AND m.max_timestamp=ticket_status.status_timestamp
WHERE
status_ID=40;
but depending on the logic of your application, it might happen that the last row in a table has a timestamp of 2014-09-27 09:41:18 and the last in another has for example 2014-09-27 09:41:19.
In this case, you should use a query like this one:
SELECT
ticket.*,
ticket_reply.*
FROM
(SELECT support_ID, MAX(status_timestamp) AS max_status_timestamp
FROM ticket_status
GROUP BY support_ID) m_status
INNER JOIN
(SELECT support_ID, MAX(reply_timestamp) AS max_reply_timestamp
FROM ticket_reply
GROUP BY support_ID) m_reply
ON m_status.support_ID=m_reply.support_ID
INNER JOIN
(SELECT support_ID, MAX(`timestamp`) AS max_ticket_timestamp
FROM ticket
GROUP BY support_ID) m_ticket
ON m_status.support_ID=m_ticket.support_ID
INNER JOIN ticket_status
ON ticket_status.support_ID=m_status.support_ID
AND ticket_status.status_timestamp=m_status.max_status_timestamp
INNER JOIN ticket_reply
ON ticket_reply.support_ID=m_reply.support_ID
AND ticket_reply.reply_timestamp=m_reply.max_reply_timestamp
INNER JOIN ticket
ON ticket.support_ID=m_ticket.support_ID
AND ticket.`timestamp`=m_ticket.max_ticket_timestamp
WHERE
ticket_status.status_ID=40;
Please see fiddle here.
You can try this one:
SELECT t.*, tr.support_reply, ts.status_timestamp
FROM ticket_status as ts
left join ticket_reply as tr on(ts.support_ID=tr.support_ID)
left join ticket as t on(t.support_ID=tr.support_ID)
where status_ID=40
order by status_timestamp desc
limit 1;

MySQL nested, nested subquery not getting outter variable

I have a spendings table and a dates table, that are joined by date_id and id...
What I'm trying to do, is get from 1 query all the info from spendings, plus the sum of all the spendings but with a limit and/or offset
This is the query right now
SELECT spendings.id, spendings.price, spendings.title,
dates.date, users.username, currencies.value,
( SELECT SUM(sum_table.price)
FROM (
SELECT s.price
FROM spendings s, dates d
WHERE s.date_id = d.id
AND day(d.date) = 25
LIMIT 2 OFFSET 0
) as sum_table
) AS sum_price
FROM spendings, dates, users, currencies
WHERE spendings.date_id = dates.id
AND day(dates.date) = 25
AND spendings.user_id = users.id
AND spendings.curr_id = currencies.id
LIMIT 2 OFFSET 0
Output
id price title date username value sum_price
3 6.00 title1 2013-11-25 alex € 21.00
4 15.00 title2 2013-11-25 alex € 21.00
It works, but only if the date here day(d.date) = 25 is the same as the outer one here day(dates.date) = 25
If instead I put day(d.date) = day(dates.date) which seems the logic thing to do, I get #1054 - Unknown column 'dates.date' in 'where clause'
If anyone has an idea to make this simpler let me know :)
Try to join instead of using nested correlated subqueries:
SELECT spendings.id, spendings.price, spendings.title,
dates.date, users.username, currencies.value,
y.sum_price
FROM spendings, dates, users, currencies
JOIN (
SELECT day, SUM(sum_table.price) As sum_price
FROM (
SELECT day(d.date) As day,
s.price
FROM spendings s, dates d
WHERE s.date_id = d.id
AND day(d.date) = 25
LIMIT 2 OFFSET 0
) sum_table
GROUP BY day
) y
ON y.day = day(dates.date)
WHERE spendings.date_id = dates.id
-- AND day(dates.date) = 25 <== commented since it's redundant now
AND spendings.user_id = users.id
AND spendings.curr_id = currencies.id
Some remarks:
Using old join syntax with commas is not recommended: FROM table1,table2,table2 WHERE
The recommended way of expressing joins is "new" ANSI SQL join syntax:
FROM table1
[left|right|cross|[full] outer|natural] JOIN table2 {ON|USING} join_condition1
[left|right|cross|[full] outer|natural] JOIN table3 {ON|USING} join_condition2
....
Actually this "new syntax" is quite old now, since is has been published, as I remember, in 1992 - 22 years ago. In IT industry 22 years is like 22 ages.

Mysql Count per column

I have the following query:
SELECT a.feeder_id, b.feeder_pr
FROM authors_article_feeders a
LEFT JOIN feeders b ON b.id = a.feeder_id
WHERE website_id =1
LIMIT 0 , 30
which results in:
feeder_id feeder_pr
18 2
18 2
18 2
18 2
32 6
What I need is to modify the above query so that it will manipulate this data so that the result would end up with a count of each feeder_pr, so in this case the result would be:
feeder_pr count
2 4
6 1
Any assistance is appreciated. If you have time please describe your solution so that I can learn from it while I'm at it.
Everything I've tried has ended in inaccurate results, usually with just one row instead of the expected 2.
You just need to add a GROUP BY And, you would not even need the joins
SELECT b.feeder_pr, COUNT(b.feeder_pr)
FROM feeders b
GROUP BY b.feeder_pr
SELECT b.feeder_pr, count(a.feeder_id) as count
FROM authors_article_feeders a
LEFT JOIN feeders b ON b.id = a.feeder_id
WHERE website_id =1
GROUP BY 1