I'm having a hard time wrapping my mind around this, any assistance is most appreciated.
I have two select statements with joins to 1 or more tables.
SELECT repinfo.repName, SUM(callstatssummary.CallsIn)
FROM repinfo
LEFT JOIN callstatssummary
ON repinfo.isaacID = callstatssummary.IsaacID AND callstatssummary.ShiftDate >= '2013-02-10' AND callstatssummary.ShiftDate <= '2013-02-16'
GROUP BY repinfo.repName;
The output of the first statement is a list of everyone in the repinfo table, with the sum of the total calls they took during the week. I used a left join to include people who didn't take calls in the result.
SELECT repinfo.repName, SUM(`1036`.afterRgu) - SUM(`1036`.priorRgu)
FROM repinfo
JOIN reporders
ON repinfo.repID = reporders.oRep
JOIN `1036`
ON reporders.workOrder = `1036`.workOrder AND `1036`.entryDate >= '2013-02-10' AND `1036`.entryDate <= '2013-02-16' AND `1036`.afterRgu >= `1036`.priorRgu
GROUP BY repinfo.repName;
The second statement outputs the number of products that each person sold during the week. The repinfo table has the information about the representative, which joins with the reporders table to match the work order. The 1036 table has detailed information about the orders.
I am looking to output something like this - essentially combine the output of the two select statements:
| repName | SUM(callstatssummary.CallsIn) | SUM(`1036`.afterRgu) - SUM(`1036`.priorRgu) |
______________________________________________________________________________________________
| Bruce W | 41 | 13 |
| Cathy M | 84 | 17 |
| Jonah S | NULL | 29 |
Any suggestions?
One way to combine those statements is to make each of them a derived-table / inline-view and join on repName.
Please note: Obviously you would want to join on a rep ID number (or whatever you call the primary key of the repinfo table) if two reps can have the same name.
select
r.repName, c.sumCallsIn, o.sumProdSold
from
repinfo r
left join (
SELECT repinfo.repName,
SUM(callstatssummary.CallsIn) sumCallsIn
FROM repinfo
LEFT JOIN callstatssummary
ON repinfo.isaacID = callstatssummary.IsaacID
AND callstatssummary.ShiftDate >= '2013-02-10'
AND callstatssummary.ShiftDate <= '2013-02-16'
GROUP BY repinfo.repName
) c
on c.repName = r.repName
left join (
SELECT repinfo.repName,
SUM(`1036`.afterRgu) - SUM(`1036`.priorRgu) sumProdSold
FROM repinfo
JOIN reporders
ON repinfo.repID = reporders.oRep
JOIN `1036`
ON reporders.workOrder = `1036`.workOrder
AND `1036`.entryDate >= '2013-02-10'
AND `1036`.entryDate <= '2013-02-16'
AND `1036`.afterRgu >= `1036`.priorRgu
GROUP BY repinfo.repName
) o
on r.repName = o.repName
order by r.repName;
Related
I have two tables like this
rooms
id | number
1 | 111
2 | 112
occupied_rooms
id | check_in | check_out | room_id
1 | 2017-10-01 | 2017-10-04 | 1
I want to get all the unoccupied rooms according to date check_in and check_out for this I tried
select r.id
, r.number
from rooms r
left join occupied_rooms o
on r.id = o.room_id
where (o.check_in not between "2017-10-05" and "2017-10-08" )
or (o.check_in >= "2017-10-05" and o.check_out <= "2017-10-08"))
but this query giving me result like this. which is incorrect.
id | number
1 | 111
What is wrong with this query?
Thank you for your any help and suggestions
Just join the two tables on the condition that the id matches and the range of intended stay overlaps with the range in the occupied_rooms table.
SELECT r.*
FROM rooms r
LEFT JOIN occupied_rooms t
ON r.id = t.id AND
('2017-10-02' <= t.check_out AND '2017-10-03' >= t.check_in)
WHERE
t.id IS NULL; -- NULL value indicates the room did not overlap
-- with any existing reservations
You can also check out this great reference question on how to deal with overlapping ranges in SQL queries. It makes the problem you are facing much simpler.
Demo
Your data in table occupied_rooms meets the first condition in "where";
check_in date(2017-10-01) is not between "2017-10-05" and "2017-10-08" and your where is or.
Thus, the result is included this data.
Can you tell us what output you expect?
Suppose I have these two tables:
|Customers | |Purchases |
---------------- -----------------
|CusID |----- |PurchaseID |
|CusName | |---<|CusID |
|CusAge | |ItemName |
|CusDateAdded | |DatePurchased |
I am needing to filter my result set by multiple ItemNames. Say I want to return all Customers who are between the ages of 18 and 24 who purchased a 'camera' AND a 'phone'. I can run the following query to obtain all records for customers between the age range:
SELECT CusID
FROM Customers AS C
JOIN Purchases AS P
ON C.CusID = P.CusID
WHERE C.CusAge BETWEEN 18 AND 24
However when it comes time to filter on the ItemName column in Purchases table how does one filter on multiple rows? Supposing it is possible without multiple queries?
SELECT C.CusID
FROM Customers AS C
JOIN Purchases AS P ON C.CusID = P.CusID
WHERE C.CusAge BETWEEN 18 AND 24
AND ItemName IN ('camera','phone')
GROUP BY C.CusID
HAVING count(distinct ItemName) = 2
Group by the customer and return only those having both items.
I believe this will answer your question: SQL FIDDLE
SELECT C.CustID,C.CustName,P.ItemName
FROM Customers AS C
JOIN Purchases AS P ON C.CustID = P.CustID
GROUP BY C.CustID,C.CustName,P.ItemName
HAVING P.ItemName IN ('camera','phone')
I have the following tables in a succession of 1-to-many relationships:
company_company, company_portfolio, building_site and statistics_meter. The area of difficulty I am having is the final table, statistics_meter.
For the benefit of this exercise, it's structure is as follows:
Records are related within the same table, with some being parent meters, and some being child meters. Where a record is a child, it will have parent_meter_id set, and building_id, which crucially, is how the table is LEFT JOIN'ed set to NULL.
id | parent_meter_id | site_ref | building_id
1 | NULL | some building | 45
2 | NULL | some other building | 45
3 | 1 | and another | NULL
4 | 1 | one another one | NULL
5 | 2 | final one | NULL
I have two requirements:
1 - count the number of parent meters where the building_id is set (which I am doing successfully)
2 - count the number of meters where the parent_meter_id matches the meter_id of those counted in (1)
Thus I would expect a result whereby (1) = 2 and (2) = 3.
Here is the SQL I've got so far...I tried fiddling around with a SUM case when but I think it's totally wrong. Is this even possible within one query?
Thanks for the help.
SELECT
building_site.id as site_id,
building_site.site_ref as building_name,
COUNT(statistics_meter.id) AS meter_count,
SUM(CASE WHEN statistics_meter.parent_meter_id = [???] THEN 1 ELSE 0 END) AS check_meter_count
FROM company_company
LEFT JOIN company_portfolio ON company_portfolio.company_id=company_company.id
LEFT JOIN building_site ON building_site.portfolio_id=company_portfolio.id
LEFT JOIN statistics_meter ON statistics_meter.building_id=building_site.id
WHERE company_company.id=41
GROUP BY building_site.id
Well if I understand you, you'll need to use a subquery to get the parent meters with a building id, and then join that to your main table.
SQL Fiddle
select
sm.id,
sm.parent_meter_id,
sm2.id as ID2,
sm.site_ref,
sm.building_id
from
statistics_meter sm
inner join (
select
id,
parent_meter_id
from
statistics_meter
where
building_id is not null) sm2
on sm.parent_meter_id = sm2.id
Not sure if this is the most efficient way to do it, but in the end I performed a left join and subquery as below and performed two counts, one COUNT() for total number to answer my requirement (2) and a COUNT(distinct) to answer my requirement (1)
SELECT
count(distinct statistics_meter.id) as meter_count,
count(statistics_meter.id) as check_meter_count
FROM company_company
LEFT JOIN company_portfolio ON company_portfolio.company_id=company_company.id
LEFT JOIN building_site ON building_site.portfolio_id=company_portfolio.id
LEFT JOIN statistics_meter ON statistics_meter.building_id=building_site.id
LEFT JOIN (select * from statistics_meter where parent_meter_id is not NULL) sm2 on sm2.parent_meter_id = statistics_meter.id
I have the following SQL query which queries my tickets, ticketThreads, users and threadStatus tables:
SELECT tickets.threadId, ticketThreads.threadSubject, tickets.ticketCreatedDate, ticketThreads.threadCreatedDate, threadStatus.threadStatus, users.name
FROM
tickets
INNER JOIN
ticketThreads
ON
tickets.threadId = ticketThreads.threadId
INNER JOIN
threadStatus
ON
ticketThreads.threadStatus = threadStatus.id
INNER JOIN
users
ON
users.id = ticketThreads.threadUserId
WHERE
tickets.ticketId = ticketThreads.lastMessage
AND
ticketThreads.threadStatus != 3
ORDER BY
tickets.ticketCreatedDate
DESC
The abridged version of what this returns is:
threadId |
----------
1 |
2 |
This works fine, and is what I expect, however to clean up the code and database slightly I need to remove the ticketThreads.lastMessage column.
If I remove the line WHERE tickets.ticketId = ticketThreads.lastMessage then this is an abridged version of what is returned:
threadId |
----------
1 |
2 |
1 |
What I need to do then is edit the query above to enable me to select the highest unique value for each threadId value in the tickets database.
I know about MAX() and GROUP BY but can't figure how to get them into my query above.
The relevant parts of the tables are shown below:
tickets
ticketId | ticketUserId | threadId
-------------------------------
1 | 1 | 1
2 | 1 | 2
3 | 1 | 1
ticketThreads
threadId | lastMessage | threadStatus
-------------------------------
1 | 3 | 4
2 | 2 | 1
I hope all the above is clear and makes sense
So you need the ticket with the highest id per each thread? Your problem is actually very easy variant of greatest record per group problem. No need for any subqueries. Basicaly you have two options, which both should perform much better than your query, the second be faster (please post the actual durations in your db!):
1. Standard compliant query, but slower:
SELECT t1.threadId, ticketThreads.threadSubject, t1.ticketCreatedDate,
ticketThreads.threadCreatedDate, threadStatus.threadStatus, users.name
FROM tickets as t1
LEFT JOIN tickets as t2
ON t1.threadId = t2.threadId AND t1.ticketId < t2.ticketId
JOIN ticketThreads ON t1.threadId = ticketThreads.threadId
JOIN threadStatus ON ticketThreads.threadStatus = threadStatus.id
JOIN users ON users.id = ticketThreads.threadUserId
WHERE t2.threadId is NULL
AND ticketThreads.threadStatus != 3
ORDER BY t1.ticketCreatedDate DESC
This one joins the tickets table two times, which can make it a bit slower for big tables.
2. Faster, but uses MySQL extension to standard SQL:
set #prev_thread := NULL;
SELECT t.threadId, ticketThreads.threadSubject, t.ticketCreatedDate,
ticketThreads.threadCreatedDate, threadStatus.threadStatus, users.name
FROM tickets as t
JOIN ticketThreads ON t.threadId = ticketThreads.threadId
JOIN threadStatus ON ticketThreads.threadStatus = threadStatus.id
JOIN users ON users.id = ticketThreads.threadUserId
WHERE ticketThreads.threadStatus != 3
AND IF(IFNULL(#prev_thread, -1) = #prev_thread := t.threadId, 0, 1)
ORDER BY t.threadId, t.ticketId DESC,
t.ticketCreatedDate DESC
Here, we perform one pass scan on ordered joined data, using auxiliary mysql variable #prev_thread to filter only the first (in the given order) ticket for each thread (the one with highest ticketId).
I need a Full Outer Join in mysql. I found a solution here: Full Outer Join in MySQL My problem is that t1 and t2 are subqueries themselves. So resulting query looks like a monster.
What to do in this situation? Should I use views instead of subqueries?
Edit:
I'll try to explain a bit more. I have orders and payments. One payment can cower multiple orders, and one order can be cowered by multiple payments. That is why I have tables orders, payments, and paymentitems. Each order has field company (which made this order) and manager (which accepted this order). Now I need to group orders and payments by company and manager and count money. So I want to get something like this:
company1 | managerA | 200 | 200 | 0
company1 | managerB | Null | 100 | 100
company1 | managerC | 300 | Null | -300
company2 | managerA | 150 | Null | -150
company2 | managerB | 100 | 350 | 250
The query, I managed to create:
SELECT coalesce(o.o_company, p.o_company)
, coalesce(o.o_manager, p.o_manager)
, o.orderstotal
, p.paymentstotal
, (coalesce(p.paymentstotal, 0) - coalesce(o.orderstotal, 0)) AS balance
FROM
(((/*Subquery A*/SELECT orders.o_company
, orders.o_manager
, sum(o_money) AS orderstotal
FROM
orders
WHERE
(o_date >= #startdate)
AND (o_date <= #enddate)
GROUP BY
o_company
, o_manager) AS o
LEFT JOIN (/*Subquery B*/SELECT orders.o_company
, orders.o_manager
, sum(paymentitems.p_money) AS paymentstotal
FROM
((payments
INNER JOIN paymentitems
ON payments.p_id = paymentitems.p_id)
INNER JOIN orders
ON paymentitems.p_oid = orders.o_id)
WHERE
(payments.p_date >= #startdate)
AND (payments.p_date <= #enddate)
GROUP BY
orders.o_company
, orders.o_manager) AS p
ON (o.o_company = p.o_company) and (o.o_manager = p.o_manager))
union
(/*Subquery A*/
right join /*Subquery B*/
ON (o.o_company = p.o_company) and (o.o_manager = p.o_manager)))
This is simplified version of my query. Real query is much more complex, that is why I want to keep it as simple as it can be. Maybe even split in to views, or may be there are other options I am not aware of.
I think the clue is in "group orders and payments by company". Break the outer join into a query on orders and another query on payments, then add up the type of money (orders or payments) for each company.
If you are trying to do a full outer join and the relationship is 1-1, then you can accomplish the same thing with a union and aggreagation.
Here is an example, pulling one column from two different tables:
select id, max(col1) as col1, max(col2) as col2
from ((select t1.id, t1.col1, NULL as col2
from t1
) union all
(select t23.id, NULL as col1, t2.col2
from t2
)
) t
group by id