emulated of full join return wrong values - mysql

I have two tables that doesn't have relationship and I do a left and right join to simulate a full join on them and select some data.
the manner of displaying data is right but values are wrong, its looks like they selected more than once.
my tables are something like this :
TABLE 1 (bargains)
trade_date ---- profit
TABLE 2 (general_cost)
date ----- cost
this is the query that i write :
select b.trade_date, coalesce(sum(b.profit),0), coalesce(sum(g.cost),0)
from bargains as b
left join general_cost as g on b.trade_date = g.date group by b.trade_date
union
select g.date, coalesce(sum(b.profit),0), coalesce(sum(g.cost),0) from
bargains as b
right join general_cost as g on b.trade_date = g.date group by g.date
this is the result of query :
for example in date 1395-9-28 the sum of profit column should be 440 and sum of cost column should be 800
if it's help you should know that there is three row with this date in bargains table and two row in general_cost table

Yes, your query duplicates the matching records because those are included in both left and the right join. You need to exclude the matching records from one of the queries. I usually exclude them from the 2nd query of the union:
select b.trade_date, coalesce(sum(b.profit),0), coalesce(sum(g.cost),0)
from bargains as b
left join general_cost as g on b.trade_date = g.date group by b.trade_date
union
select g.date, coalesce(sum(b.profit),0), coalesce(sum(g.cost),0) from
bargains as b
right join general_cost as g on b.trade_date = g.date
where b.date is null //include only the records from general_cost that are not matched
group by g.date
UPDATE
If you have multiple records in both tables with the same date, then you need to do the summing per table separately in subqueries, otherwise the matching records do get duplicated:
select b.trade_date, b.profit, coalesce(g.cost,0)
from (select trade_date, sum(profit) as profit from bargains group by trade_date) as b
left join (select date, sum(cost) as cost from general_cost group by date) as g on b.trade_date = g.date
union
select g.date, 0, sum(g.cost) from //all profits has been summed up in the above query, so here we can use 0 in place of profit
bargains as b
right join general_cost as g on b.trade_date = g.date
where b.trade_date is null //include only the records from general_cost that are not matched
group by g.date

Related

Combine and Display query result

I have 2 query:
SELECT CustomerID,count(b.BookingStatus) as 'NotComplete'
FROM Booking b, Customer c
WHERE c.CustomerID=b.BookingCustomerID
AND(b.BookingStatus='Pending'
OR b.BookingStatus='OTW')
GROUP BY c.CustomerID
SELECT c.CustomerID, r.*
FROM Customer c,Regular r
WHERE c.CustomerID=r.RegularCID
Result:
1st query
2nd query
How to combine these 2 result together?
also, display the zero(count) as well.
Thanks!
this is what I get after few hours of trying..obviously it's not what I want..
SELECT c.CustomerID,count(b.BookingStatus) as 'NotComplete',r.RegularID
FROM Booking b, Customer c
JOIN Regular r on r.RegularCID=c.CustomerID
WHERE c.CustomerID=b.BookingCustomerID
AND (b.BookingStatus='Pending'
or b.BookingStatus='OTW'
or b.BookingStatus='Started'
or b.BookingStatus='Unclaimed'
or b.BookingStatus='Confirmed')
GROUP by r.RegularID
You can JOIN to the Regular table and then LEFT JOIN to a derived table of counts in the Booking table. We do it this way to avoid having to GROUP BY all the columns in the Regular table:
SELECT c.CustomerID, r.*, b.NotComplete
FROM Customer c
JOIN Regular r ON r.RegularCID = c.CustomerID
LEFT JOIN (SELECT BookingCustomerID, COUNT(*) AS NotComplete
FROM Booking
WHERE BookingStatus IN ('Pending', 'OTW', 'Started', 'Unclaimed', 'Confirmed')
GROUP BY BookingCustomerID) b ON b.BookingCustomerID = c.CustomerID
Use join on regular table, and subquery on your first select
SELECT t1.*, r.RegularCID FROM (
SELECT CustomerID,count(b.BookingStatus) as 'NotComplete',
FROM Booking b
INNER JOIN Customer c ON c.CustomerID=b.BookingCustomerID
WHERE (b.BookingStatus='Pending' OR b.BookingStatus='OTW')
GROUP BY c.CustomerID) t1
LEFT JOIN Regular r on r.CustomerID = t1.CustomerID

Group and count rows and then use them in condition

I have an query like:
SELECT * FROM account AS a
LEFT JOIN (SELECT SUM(bill.amount) total, bill.accountId FROM bill GROUP BY bill.accountId) b ON a.id = b.accountId
WHERE a.partner_id = 1 OR a.partner_id = 2
How can I check, how many groups in "bill" has the same a.partner_id?
For example: 3 groups has partner_id = 1, 2 groups has partner_id = 2.
And later include to left join only groups, if more than 2 groups have the same partner_id.
If I understand correctly, you just want an aggregation on top of your query:
SELECT a.partner_id, count(*) as cnt, sum(total) as total
FROM account a LEFT JOIN
(SELECT SUM(b.amount) as total, b.accountId
FROM bill b
GROUP BY b.accountId
) b
ON a.id = b.accountId
GROUP BY a.partner_id;
You should be able to use the "HAVING" clause. Below is an example from the following link:
https://dev.mysql.com/doc/refman/5.0/en/group-by-handling.html
SELECT name, COUNT(name) AS c FROM orders
GROUP BY name
HAVING c = 1;

mySQL Sub Select needed

I have three tables, libraryitems, copies and loans.
A libraryitem hasMany copies, and a copy hasMany loans.
I'm trying to get the latest loan entry for a copy only; The query below returns all loans for a given copy.
SELECT
libraryitems.title,
copies.id,
copies.qruuid,
loans.id AS loanid,
loans.status,
loans.byname,
loans.byemail,
loans.createdAt
FROM copies
INNER JOIN libraryitems ON copies.libraryitemid = libraryitems.id AND libraryitems.deletedAt IS NULL
LEFT OUTER JOIN loans ON copies.id = loans.copyid
WHERE copies.libraryitemid = 1
ORDER BY copies.id ASC, loans.createdAt DESC
I know there needs to be a sub select of some description in here, but struggling to get the correct syntax. How do I only return the latest, i.e MAX(loans.createdAt) row for each distinct copy? Just using group by copies.id returns the earliest, rather than latest entry.
Image example below:
in the subquery , getting maximum created time for a loan i.e. latest entry and joining back with loans to get other details.
SELECT
T.title,
T.id,
T.qruuid,
loans.id AS loanid,
loans.status,
loans.byname,
loans.byemail,
loans.createdAt
FROM
(
SELECT C.id, C.qruuid, L.title, MAX(LN.createdAt) as maxCreatedTime
FROM Copies C
INNER JOIN libraryitems L ON C.libraryitemid = L.id
AND L.deletedAt IS NULL
LEFT OUTER JOIN loans LN ON C.id = LN.copyid
GROUP BY C.id, C.qruuid, L.title) T
JOIN loans ON T.id = loans.copyid
AND T.maxCreatedTime = loans.createdAt
A self left join on loans table will give you latest loan of a copy, you may join the query to the other tables to fetch the desired output.
select * from loans A
left outer join loans B
on A.copyid = B.copyid and A.createdAt < B.createdAt
where B.createdAt is null;
This is your query with one simple modification -- table aliases to make it clearer.
SELECT li.title, c.id, c.qruuid,
l.id AS loanid, l.status, l.byname, l.byemail, l.createdAt
FROM copies c INNER JOIN
libraryitems li
ON c.libraryitemid = li.id AND
li.deletedAt IS NULL LEFT JOIN
loans l
ON c.id = l.copyid
WHERE c.libraryitemid = 1
ORDER BY c.id ASC, l.createdAt DESC ;
With this as a beginning let's think about what you need. You want the load with the latest createdAt date for each c.id. You can get this information with a subquery:
select l.copyid, max(createdAt)
from loans
group by l.copyId
Now, you just need to join this information back in:
SELECT li.title, c.id, c.qruuid,
l.id AS loanid, l.status, l.byname, l.byemail, l.createdAt
FROM copies c INNER JOIN
libraryitems li
ON c.libraryitemid = li.id AND
li.deletedAt IS NULL LEFT JOIN
loans l
ON c.id = l.copyid LEFT JOIN
(SELECT l.copyid, max(l.createdAt) as maxca
FROM loans
GROUP BY l.copyid
) lmax
ON l.copyId = lmax.copyId and l.createdAt = lmax.maxca
WHERE c.libraryitemid = 1
ORDER BY c.id ASC, l.createdAt DESC ;
This should give you the most recent record. And, the use of left join should keep all copies, even those that have never been leant.

MySQL Help Cleaning Total Counts

Having an issue with a JOIN statement.
I'm trying to get a total per name, and not the current 1 with a ton of other same name records
SELECT a.`name`,
(SELECT COUNT(b.`id`)
FROM `host1_hosting` AS b
WHERE b.`id` = c.`host1_servers_host1_hosting_1host1_hosting_idb`) AS HostingCount
FROM `host1_servers` AS a
LEFT JOIN `host1_servers_host1_hosting_1_c` AS c ON c.`host1_servers_host1_hosting_1host1_servers_ida` = a.`id`
ORDER BY a.`name`
Example Returned
Name HostingCount
Name 1
Name 1
Name 1
Where it should be:
Name 3
I'm sure this is simple, but it's early monday, and I'm foggy
Query 2
SELECT a.`name`, COUNT(d.`id`)
FROM `host1_servers` AS a
JOIN `host1_servers_host1_hosting_1_c` AS c ON c.`host1_servers_host1_hosting_1host1_servers_ida` = a.`id`
JOIN `host1_hosting` AS d ON d.`id` = c.`host1_servers_host1_hosting_1host1_hosting_idb`
ORDER BY a.`name`
Gets me 1 name record, but a total of all COUNT
Your second query needs a group by:
SELECT a.`name`, COUNT(d.`id`)
FROM `host1_servers` AS a
JOIN `host1_servers_host1_hosting_1_c` AS c ON c.`host1_servers_host1_hosting_1host1_servers_ida` = a.`id`
JOIN `host1_hosting` AS d ON d.`id` = c.`host1_servers_host1_hosting_1host1_hosting_idb`
GROUP BY a.name
ORDER BY a.`name`;
Without the GROUP BY, MySQL interprets the query as an aggregation query to produce one row. The count() is the overall count. The column name is chosen arbitrarily from one of the rows (using a MySQL extension that wouldn't work in any other database).
EDIT:
If you want to keep all names from the first table and do the count, use left outer join:
SELECT a.`name`, COUNT(d.`id`)
FROM `host1_servers` a LEFT OUTER JOIN
`host1_servers_host1_hosting_1_c` c
ON c.`host1_servers_host1_hosting_1host1_servers_ida` = a.`id` LEFT OUTER JOIN
`host1_hosting` d
ON d.`id` = c.`host1_servers_host1_hosting_1host1_hosting_idb`
GROUP BY a.name
ORDER BY a.`name`;

group by count from left joined table

Here's my query:
$select d.*,
count(a.id) as delivered
from `dealerships` as d
left join `assignments` as a on (a.id_dealership = d.id)
group by d.id
order by d.name asc
now this works, but it is counting duplicate leads. when I add a.id_lead to the group by, it messes up everything. There is a column in the assignments table called id_lead and I want the count() (delivered) to count the total of assignments grouped by id_lead, so that it ignores more than 1 row with the same id_lead.
Is this what you mean? :
select d.*,
count(distinct a.id_lead) as delivered
from `dealerships` as d
left join `assignments` as a on (a.id_dealership = d.id)
group by d.id
order by d.name asc
It's the same as your query, except that instead of counting the total number of records in a, it will only count the number of distinct non-null values in a.id_lead.
(If that's not what you mean, then please clarify.)