Ordering search of joined tables in 2 ways - php

I am trying to search for the oldest outstanding invoice for each customer and then present the results in descending order of that invoice date however the result gives me the date of the most recent invoice. How can I ensure that it picks up the earliest invoice date? Many thanks in advance.
Here is my query:
$checkDebtors = "select
a.accountNumber as accountNumber, a.balanceOutstanding as balanceOutstanding, a.companyName as companyName, b.invoiceDate as invoiceDate, b.netOutstanding as netOutstanding
from
customersQQuote a
Right JOIN invoices b ON a.accountNumber = b.accountNumber
WHERE netOutstanding > 0 AND balanceOutstanding > 0
group by
a.accountNumber
order by
b.invoiceDate ASC";

Did you mean the result by following sql? Try it, if it does not work for you, leave a comment for me;)
select
a.accountNumber as accountNumber,
a.balanceOutstanding as balanceOutstanding,
a.companyName as companyName,
b.invoiceDate as invoiceDate,
c.netOutstanding as netOutstanding
from
(SELECT accountNumber, MIN(invoiceDate) AS invoiceDate FROM invoices GROUP BY accountNumber) b
LEFT JOIN customersQQuote a ON a.accountNumber = b.accountNumber
LEFT JOIN invoices c ON c.accountNumber = b.accountNumber AND c.invoiceDate = b.invoiceDate
WHERE c.netOutstanding > 0 AND a.balanceOutstanding > 0
group by
a.accountNumber
order by
b.invoiceDate ASC

Above code has certain mistakes such as, alias name using in where clause and misuse of group by clause. you can apply this piece of code..
select a.accountNumber as accountNumber, a.balanceOutstanding as balanceOutstanding,
a.companyName as companyName, b.invoiceDate as invoiceDate, b.netOutstanding as netOutstanding
from customersQQuote a Right JOIN invoices b ON a.accountNumber = b.accountNumber
WHERE b.netOutstanding > 0 AND a.balanceOutstanding > 0 order by b.invoiceDate;

Your current code does not work as you have not specified which invoice to pull back. You have used GROUP BY accountNumber, so it will return one row for each accountNumber value. Other fields should be aggregate values (ie, the result of a aggregate function such as MIN() or MAX()). If fields are neither mentioned in the GROUP BY clause nor the result of an aggregate function then MySQL will return a value for that column, but which row that value comes from is not defined. It could be the first or the last rows value, or any in between (and could change between different versions of MySQL).
Most flavours of SQL do not allow a query like this and would error. MySQL has a feature that does allow this. But this feature can be configured to be turned off.
To do this in standard SQL and give you a consistent value you use a sub query to get the first invoiceDate for each accountNumber, using the MIN() aggregate function. Then you join this sub query against the invoices table to get the other columns.
SELECT a.accountNumber,
a.balanceOutstanding,
a.companyName,
b.invoiceDate,
b.netOutstanding
FROM
(
SELECT accountNumber,
MIN(invoiceDate) as invoiceDate
FROM invoices
GROUP BY accountNumber
) sub0
INNER JOIN invoices b ON sub0.account_number = b.account_number AND sub0.invoiceDate = b.invoiceDate
INNER JOIN customersQQuote a ON sub0.account_number = a.account_number
Note that if an accountNumber could have 2 identical first invoiceDate values then you will need to pick another field to minimise (ie, maybe your unique id field) for each date. Gets messy but something like this:-
SELECT a.accountNumber,
a.balanceOutstanding,
a.companyName,
b.invoiceDate,
b.netOutstanding
FROM
(
SELECT accountNumber,
MIN(invoiceDate) as invoiceDate
FROM invoices
GROUP BY accountNumber
) sub0
INNER JOIN
(
SELECT accountNumber,
invoiceDate,
MIN(id) as id
FROM invoices
GROUP BY accountNumber, invoiceDate
) sub1 ON sub0.accountNumber = sub1.accountNumber AND sub0.invoiceDate = sub1.invoiceDate
INNER JOIN invoices b ON sub1.account_number = b.account_number AND sub1.invoiceDate = b.invoiceDate AND sub1.id = b.id
INNER JOIN customersQQuote a ON sub0.account_number = a.account_number

Thank you for your advice. I got some good tips from your answers which helped me solve the problem. The main thing being the use of MIN() and also that I was using Right join rather than Left. Additionally I just used the invoiceDate keyword in the ORDER BY rather than the b.invoiceDate. I guess this ensured the MIN rule was used here as well. Thanks to you I ended up getting the result I needed.
select
a.accountNumber as accountNumber, a.balanceOutstanding as balanceOutstanding, a.companyName as companyName, MIN(b.invoiceDate) as invoiceDate, b.netOutstanding as netOutstanding
from
customersQQuote a
Left JOIN invoices b ON a.accountNumber = b.accountNumber
WHERE netOutstanding > 0 AND balanceOutstanding > 0
group by
a.accountNumber
order by invoiceDate ASC

Related

Left Join - Select ALL from left table but select only the latest from right table

I have 2 tables, borrowers and loans. I want to display on the main page the list of ALL borrowers with or without loans. If with loan, display the newest one.
I have the following sql query, basically it returns the above description except it displays the very first loan of the borrower instead of the latest one.
(Side note: I used GROUP BY to avoid duplicates. Without it the query returns duplicated borrower names if they have multiple loans. Just wanted to know if this is an efficient way of doing so.)
SELECT b.b_id,
b.isdeleted,
b.picture,
b.firstname,
b.middlename,
b.lastname,
b.address,
b.contactno,
b.birthday,
b.businessname,
b.occupation,
b.comaker,
b.comakerno,
b.remarks,
b.datecreated,
b.activeloan,
l.l_id,
l.amount,
l.payable,
l.balance,
l.mode,
l.term,
l.interestrate,
l.amortization,
l.releasedate,
l.duedate,
l.status,
l.c_id
FROM borrowers as b
LEFT JOIN loans as l ON b.b_id = l.b_id
WHERE b.isdeleted = 0
GROUP BY b.b_id
It seems the below query does exactly what i wanted.
I added the below subquery on the "ON" clause.
(SELECT MAX(l_id)
FROM jai_db.loans as l2
WHERE l2.b_id = b.b_id LIMIT 1)
SELECT b.b_id, b.isdeleted, b.picture, b.firstname, b.middlename, b.lastname, b.address, b.contactno,
b.birthday, b.businessname, b.occupation, b.comaker, b.comakerno, b.remarks, b.datecreated, b.activeloan,
l.l_id, l.amount, l.payable, l.balance, l.mode, l.term, l.interestrate, l.amortization,
l.releasedate, l.duedate, l.status, l.c_id
FROM jai_db.borrowers as b
LEFT JOIN jai_db.loans as l
ON l.l_id = (SELECT MAX(l_id)
FROM jai_db.loans as l2
WHERE l2.b_id = b.b_id LIMIT 1)
WHERE b.isdeleted = 0

Query is not showing up 0 values mysql

In my query I am listing all of the theater ticket sales and movie ticket sales of different customers. The issue I'm running into is that all of the '0' ticket sales, so those users who haven't boughten a theater ticket or movie ticket is not showing up.
Here's a picture for a visual aspect: table
I believe I need to be doing a union to return the users who haven't boughten any tickets. I just can't seem to figure this out.
Thanks in advance.
Here's my code so far:
select customer.hippcode, customer.LastName, customer.Firstname, customer.Email,
count(ticketdetails.eventtype) as 'Theater Tickets',
0 as 'Movie Tickets'
from customer
inner join ticketdetails on ticketdetails.hippcode = customer.hippcode
where ticketdetails.hippcode is not null
and ticketdetails.eventType ='T'
Group by Customer.hippcode
union
select customer.hippcode, customer.LastName, customer.Firstname, customer.Email,
0 as 'Theater Tickets', count(ticketdetails.eventtype) as 'Movie Tickets'
from customer
inner join ticketdetails on ticketdetails.hippcode = customer.hippcode
where ticketdetails.hippcode is not null
and ticketdetails.eventType ='M'
Group by Customer.hippcode
order by `theater tickets` + `movie tickets` desc;
select
customer.hippcode, customer.LastName, customer.Firstname, customer.Email,
sum(case when ticketdetails.eventtype = 'T' then 1 else 0 end) as TheaterTickets,
sum(case when ticketdetails.eventtype = 'M' then 1 else 0 end) as MovieTickets
from customer
inner join ticketdetails on ticketdetails.hippcode = customer.hippcode
where ticketdetails.hippcode is not null
and ticketdetails.eventType in ('T', 'M')
Group by customer.hippcode, customer.LastName, customer.Firstname, customer.Email
Order by 'TheaterTickets' + 'MovieTickets' desc
inner join => bring the line only if you got a record on both table.
I think you should use a LEFT JOIN somewhere to chose the master table
http://dev.mysql.com/doc/refman/5.7/en/join.html and
http://dev.mysql.com/doc/refman/5.7/en/left-join-optimization.html
I think the last query is the only one you want. A left join is appropriate, but you need to be careful about the where clause:
select c.hippcode, c.LastName, c.Firstname, c.Email,
sum(td.eventtype) as TheaterTickets,
sum(td.eventtype) as MovieTickets
from customer c left join
ticketdetails td
on td.hippcode = c.hippcode and
td.eventType in ('T', 'M')
Group by c.hippcode, c.LastName, c.Firstname, c.Email
Order by count(t.hippcode) desc;
Notes:
Table aliases make the query easier to read and write.
Conditions on ticketdetails go in the on clause, not the where clause.
The condition on td.hippcode is not null is unnecessary, because NULL will not match in the join (note: you might want to check for the customer column).
case is the standard way to do the conditional sum (and hence correct). However, MySQL offers a much simpler and intuitive syntax.
Your order by was doing nothing, because it was adding two strings (hence equivalent to order by 0. Don't use single quotes ever for column names, and you won't have a problem like that.

mysql query become slow when data reached upto 100000

I build a query a month ago on a website. It was working fine. But after a month I was informed that the website become very slow to load the page.
When I search for the problem, I found that my query is executing very slow to fetch the data from mysql database. Then I check for the database and found that the 4 tables which I was using by joins, have around 216850, 167634, 64000, 931 rows respectively.
I have already have indexed that tables. So, where I'm lacking. Please help guys.
[Edit]
Table1: user_alert
Records: 216850
DB Type: InnoDB
Indexes: id(primary)
Table2: orders
Records: 167634
DB Type: InnoDB
Indexes: id(primary), order_id, customer_id
Table3: user_registration
Records: 64000 around
DB Type: InnoDB
Indexes: id(primary), email_address
Table4: cities
Records: 931
DB Type: InnoDB
Indexes: id(primary)
Query:
SELECT uas.alert_id, uas.user_id, uas.status, ur.first_name, ur.last_name, ur.email_address, o.order_id,
CASE WHEN ct.city_name IS NULL THEN uas.city_name ELSE ct.city_name END AS city_name
FROM `user_alert` uas
LEFT JOIN orders o ON o.customer_id = uas.user_id
LEFT JOIN user_registration ur ON ur.id = uas.user_id
LEFT JOIN `cities` ct ON ct.city_id = uas.city_id
WHERE uas.status = '1'
GROUP BY uas.user_id
ORDER BY uas.create_date DESC
GROUP BY is used to aggregate values up. For example if you wanted the count of orders by a user you could use COUNT(o.order_id).....GROUP BY uas.user_id. There are multiple orders for each user, but the aggregate function is just counting them here. However if you just select o.order_id when you have a GROUP BY uas.user_id it doesn't know which of the possibly many order_id values to return for that user id.
In this case it possibly doesn't matter as it looks like the order table is the only one where there is multiple rows per use. If you want the latest one you could just use MAX(o.order_id) (assuming that the order_id is assigned is order). But if you wanted the order value it becomes more difficult.
SELECT uas.alert_id, uas.user_id, uas.status, ur.first_name, ur.last_name, ur.email_address, MAX(o.order_id) AS LatestOrderId,
IFNULL(ct.city_name, uas.city_name) AS city_name
FROM `user_alert` uas
LEFT JOIN orders o ON o.customer_id = uas.user_id
LEFT JOIN user_registration ur ON ur.id = uas.user_id
LEFT JOIN `cities` ct ON ct.city_id = uas.city_id
WHERE uas.status = '1'
GROUP BY uas.user_id
ORDER BY uas.create_date DESC
If you wanted the (say) value of the latest order then it becomes more difficult.
SELECT uas.alert_id, uas.user_id, uas.status, ur.first_name, ur.last_name, ur.email_address, Sub1.MaxOrderId AS LatestOrderId, o.order_value
IFNULL(ct.city_name, uas.city_name) AS city_name
FROM `user_alert` uas
LEFT JOIN (SELECT customer_id, MAX(order_id) AS MaxOrderId FROM orders GROUP BY customer_id) Sub1 ON Sub1.customer_id = uas.user_id
LEFT OUTER JOIN orders o ON o.customer_id = Sub1.user_id AND o.order_id = Sub1.MaxOrderId
LEFT JOIN user_registration ur ON ur.id = uas.user_id
LEFT JOIN `cities` ct ON ct.city_id = uas.city_id
WHERE uas.status = '1'
ORDER BY uas.create_date DESC
Or doing a bit of a fiddle based on GROUP_CONCAT
SELECT uas.alert_id, uas.user_id, uas.status, ur.first_name, ur.last_name, ur.email_address,
SUBSTRING_INDEX(GROUP_CONCAT(o.order_id ORDER BY o.order_id DESC), ',', 1) AS LatestOrderId,
SUBSTRING_INDEX(GROUP_CONCAT(o.order_value ORDER BY o.order_id DESC), ',', 1) AS LatestOrderValue,
IFNULL(ct.city_name, uas.city_name) AS city_name
FROM `user_alert` uas
LEFT OUTER JOIN orders o ON o.customer_id = uas.user_id AND o.order_id = Sub1.MaxOrderId
LEFT JOIN user_registration ur ON ur.id = uas.user_id
LEFT JOIN `cities` ct ON ct.city_id = uas.city_id
WHERE uas.status = '1'
GROUP BY uas.user_id
ORDER BY uas.create_date DESC

Getting all different values but not duplicates from multiple records

I have the following SQL:
SELECT o.order_id, ol.product_manufacturer
FROM orders o
INNER JOIN order_lines ol
ON o.order_id = ol.order_id
WHERE o.deadline_time > UNIX_TIMESTAMP()
I also have these tables:
orders:
order_id, somefield, somefield, somefield...
order_lines:
id, order_id, product_manufacturer, somefield, somefield...
I want to get all orders where deadline hasn't passed and group them by product_manufacturer, but if an order has three order lines, that all have the product_manufacturer id, then it should only return the id ones. If it has the two order lines with different product_manufacturer id, then it should return the id twice and so on.
I'm using PHP and I would like to end up with an array like this:
$group[manufacturer_id] = array(order_id, order_id, order_id);
EDIT: Actually I want it to return manufacturer_id = 999, if there are multiple manufacturer ids in an order. Sorry for the mistake.
EDIT2: I'm making a listing of all orders, but I have to list them by their manufacturer. Like so:
SONY
Order 1
Order 2
Order 16
Order 99
PROSONIC
Order 3
Order 88
Order 98
Later it should be possible to search, so I will have to make something like WHERE ol.product_name LIKE '%$query', but I will add that later on, when it works.
SELECT o.order_id,group_concat(distinct(ol.product_manufacturer )) as manufacturer
FROM orders o
INNER JOIN order_lines ol
ON o.order_id = ol.order_id
WHERE o.deadline_time > UNIX_TIMESTAMP()
group by o.order_id
This will return
Order_id manufacturer
1 sony, panasonic
2 sony
SELECT o.order_id,
CASE WHEN count(distinct(ol.product_manufacturer )) >1
THEN '999' ELSE product_manufacturer END as manufacturerid
FROM orders o
INNER JOIN order_lines ol
ON o.order_id = ol.order_id
WHERE o.deadline_time > UNIX_TIMESTAMP()
group by o.order_id
This will return
Order_id manufacturerid
1 999
2 sony
As far as I can see you cannot get that form of output from a single SQL command.
I suggest you to get SELECT DISTINCT product_manufacturer FROM order_lines and then search from all orders where deadline hasn't passed for each product_manufacturer.
For ex:
SELECT o.order_id FROM orders o, order_lines ol
WHERE o.deadline_time > UNIX_TIMESTAMP()
AND ol.product_manufacturer='$pro_man'
#UlrikMcArdle
why isn't the first query with ORDER BY what you are looking for?
SELECT o.order_id, ol.product_manufacturer
FROM orders o
INNER JOIN order_lines ol ON o.order_id = ol.order_id
WHERE o.deadline_time > UNIX_TIMESTAMP()
ORDER ol.product_manufacturer, o.order_id
SELECT ol.product_manufacturer
FROM orders o
INNER JOIN order_lines ol
ON o.order_id = ol.order_id
WHERE o.deadline_time > UNIX_TIMESTAMP()
GROUP BY ol.product_manufacturer
you can use some sort of UDF to concat all the order_id for the manufacture.
Click here for similar UDF

Issue with group by and order by clause with inner join

I have two tables namely locations and pilots. I have been trying to fetch data based on location_id i.e selecting pilots who are flying in a particular location_id order by date(date of schedule).
I am using group by as i need only distinct pilots to be displayed.
select B.*,
A.rather_to_be_flying_now,
A.here_now,
A.flying,
A.when,
A.my_favorite,
A.start,
A.end,
A.Locationid
from locations A
inner join pilots B
on A.Pilotid=B.pilot_id
where A.VenueID='$venueid'
and (A.flying='1' or A.here_now='1')
group by A.Pilotid
ORDER BY A.start
The query works good if i wont include a group by clause. It returns the following result
with out group by clause
with group by clause
But the above table shows wrong order, as the output must return start time as 2013-01-24 02:00:00 for pilotid 1 (Chronological order).
You can use MIN()
select B.*,
A.rather_to_be_flying_now,
A.here_now,
A.flying,
A.when,
A.my_favorite,
MIN(A.start) as start,
A.end,
A.Locationid
from locations A
inner join pilots B
on A.Pilotid=B.pilot_id
where A.VenueID='$venueid'
and (A.flying='1' or A.here_now='1')
group by A.Pilotid
ORDER BY A.start
Try this instead:
SELECT
B.*,
A.rather_to_be_flying_now,
A.here_now,
A.flying,
A.when,
A.my_favorite,
A.start,
A.end,
A.Locationid
FROM locations A
INNER JOIN
(
SELECT pilotid, MIN(start) MinStart
FROM locations
GROUP BY pilotid
) a2 ON A.pilotId = a2.pilotId
AND a.start = a2.minStart
INNER JOIN pilots B on A.Pilotid = B.pilot_id
WHERE A.VenueID = '$venueid'
AND (A.flying='1' OR A.here_now='1');
ORDER BY A.start ;
This will give you only those pilots with the minimum start date.
Try this query -
SELECT
p.pilit_id, l.location_id, l.start
FROM pilots p
JOIN (SELECT l1.*
FROM locations l1
JOIN (SELECT location_id, MIN(start) start
FROM locations
GROUP BY locations) l2
ON l1.id = l2.id AND l1.start = l2.start
) l
ON l.pilot_id = p.pilot_id
GROUP BY p.pilot_id
Add your WHERE condition.

Categories