I have the following query which works (without the "WHERE stats.dt" part). I get all users with their data.
My problem is that this query of course results in rows ONLY with users that have stats.dt > $timestampnow-$maxdays_data). But I need ALL users but their values of SUM(upload) or SUM(download) need only to be fetched when stats.dt is larger than tstamp-maxdays. The other rows with values of upload and download where stats.dt is smaller than what I need, can be ignored.
An example would be that the user with nodeid 2 would not be selected because his dt is too small. I do want the user to be selected but just not with data or upload values (they can be 0).
The stats table looks like this
nodeid | dt | upload | download
----------------------------------------
1 | 1381699533 | 345345 | 42324234
1 | 1382899152 | 7575 | 574234
1 | 1380699533 | 764534 | 7235232
2 | 1372899152 | 71455 | 124123
I don't know where to start looking how to solve this so maybe somebody out there can point me in the right direction. Thanks!
SELECT b.id, b.lastname, b.name, c.balance, a.maxdebt, b.warndata, b.warndownload, b.warnupload, b.warndebt, b.cutoffdata, b.cutoffdownload, b.cutoffupload, b.cutoffdebt, b.data, b.download, b.upload, b.warning, b.access, b.cutoffstop
FROM (
SELECT customers.id AS id, SUM(tariffs.value) AS maxdebt
FROM tariffs
LEFT JOIN assignments ON tariffs.id = assignments.tariffid
RIGHT JOIN customers ON assignments.customerid = customers.id
GROUP BY id
) a
JOIN (
SELECT customers.id AS id, UPPER(lastname) AS lastname, customers.name AS name, SUM(stats.upload+stats.download) AS data, SUM(stats.download) AS download, SUM(stats.upload) AS upload, customers.cutoffstop, warndata, warndownload, warnupload, warndebt, cutoffdata, cutoffdownload, cutoffupload, cutoffdebt, nodes.warning, nodes.access
FROM customers
LEFT JOIN nodes ON customers.id = nodes.ownerid
LEFT JOIN stats ON nodes.id = stats.nodeid
LEFT JOIN customerwarnings ON customers.id = customerwarnings.id
WHERE stats.dt > ($timestampnow-$maxdays_data)
GROUP BY id
) b ON a.id = b.id
JOIN (
SELECT customerid, SUM(cash.value) AS balance
FROM cash
GROUP BY customerid
) c ON b.id = c.customerid
Here's a brute force way of doing it. It can almost certainly be simplified, but without knowing more about the table and foreign key structures it's hard to be sure.
What I've done is replace sum(stats.download) with sum(case when stats.dt > ($timestampnow-$maxdays_data) then s.download end) and similarly for upload. I've also changed the join on b to be an outer join:
Select
b.id,
b.lastname,
b.name,
c.balance,
a.maxdebt,
b.warndata,
b.warndownload,
b.warnupload,
b.warndebt,
b.cutoffdata,
b.cutoffdownload,
b.cutoffupload,
b.cutoffdebt,
b.data,
b.download,
b.upload,
b.warning,
b.access,
b.cutoffstop
From (
Select
c.id,
sum(t.value) as maxdebt
From
tariffs t
left join
assignments a
on t.id = a.tariffid
right join
customers
on a.customerid = c.id
Group by
c.id
) a left outer join (
Select
c.id,
upper(lastname) as lastname,
c.name,
sum(s.upload + s.download) as data,
sum(case when stats.dt > ($timestampnow-$maxdays_data) then s.download end) as download,
sum(case when stats.dt > ($timestampnow-$maxdays_data) then s.upload end) as upload,
c.cutoffstop,
warndata,
warndownload,
warnupload,
warndebt,
cutoffdata,
cutoffdownload,
cutoffupload,
cutoffdebt,
n.warning,
n.access
From
customers c
left join
nodes n
on c.id = n.ownerid
left join
stats s
on n.id = s.nodeid
left join
customerwarnings w
on c.id = w.id
Group By
c.id
) b
On a.id = b.id
inner join (
Select
customerid,
sum(cash.value) as balance
From
cash
Group By
customerid
) c
on a.id = c.customerid
Related
I am trying to fetch the value of available stock in MYSQL but when the number of rows gets more than 35000 request is failing with no response. I use subqueries to fetch the value of the available stock.
Here is my code:
SELECT
p.`product-id` AS id,
p.`product-id` AS product_id,
ped.purchase_entry_id AS purchase_id,
p.`product-name` AS name,
m.name AS manufacturer,
p.`product-type` AS product_type,
ped.id AS batch_no,
ped.internal_sales_rate AS barcode,
c.name as category,
ped.size AS size,
IFNULL(ped.sales_rate,0) AS sales_rate,
IFNULL(ped.purchase_rate,0) AS purchase_rate,
IFNULL(SUM(ped.units),0) AS units_purchased,
(select IFNULL(SUM(sed.qty),0) FROM sales_entry_details sed WHERE (sed.batch_no = ped.id)) AS units_sold,
(select IFNULL(SUM(sr.qty),0) FROM sales_return_item sr WHERE (sr.batch_no = ped.id)) AS retured,
(select IFNULL(SUM(pri.qty),0) FROM purchase_return_items pri WHERE (pri.batch_no = ped.id)) AS purchase_return,
(select IFNULL(SUM(ast.qty),0) FROM adjustment_stock ast WHERE (ast.batch_no = ped.id)) AS adjustment,
(select IFNULL(SUM(ast2.batch_no),0) FROM adjustment_stock ast2 WHERE (ast2.batch_no = ped.id)) AS isChecked
FROM purchase_entry_details ped
LEFT JOIN products p on p.`product-id` = ped.product_id
LEFT JOIN category c ON c.id = p.`product-type`
LEFT JOIN manufacturer m ON m.id = p.manufacturer
GROUP BY ped.id;
Is there any better option to fetch the value of stock available in SQL??
The query looks fine. As there is no limiting criteria (no WHERE clause), you'll read the whole table sequentially.
You'd want indexes on batch_no in the involved tables of course. But I guess these are foreign keys to some batch table, so the indexes should already exist.
The only thing that I notice is that you select from adjustment_stock twice. You can avoid this by moving the subqueries to your FROM clause:
SELECT
p.`product-id` AS id,
p.`product-id` AS product_id,
ped.purchase_entry_id AS purchase_id,
p.`product-name` AS name,
m.name AS manufacturer,
p.`product-type` AS product_type,
ped.id AS batch_no,
ped.internal_sales_rate AS barcode,
c.name as category,
ped.size AS size,
IFNULL(ped.sales_rate,0) AS sales_rate,
IFNULL(ped.purchase_rate,0) AS purchase_rate,
IFNULL(SUM(ped.units),0) AS units_purchased,
IFNULL(sed.sum_qty),0) AS units_sold,
IFNULL(sr.sum_qty),0) AS retured,
IFNULL(pri.sum_qty),0) AS retured,
IFNULL(ast.sum_qty),0) AS adjustment,
IFNULL(ast.sum_batch_no),0) AS isChecked
FROM purchase_entry_details ped
LEFT JOIN products p on p.`product-id` = ped.product_id
LEFT JOIN category c ON c.id = p.`product-type`
LEFT JOIN manufacturer m ON m.id = p.manufacturer
LEFT JOIN
(
select batch_no, sum(qty) as sum_qty from sales_entry_details group by batch_no
) sed on sed.batch_no = ped.id
LEFT JOIN
(
select batch_no, sum(qty) as sum_qty from sales_return_item group by batch_no
) sr on sr.batch_no = ped.id
LEFT JOIN
(
select batch_no, sum(qty) as sum_qty from purchase_return_items group by batch_no
) pri on pri.batch_no = ped.id
LEFT JOIN
(
select batch_no, sum(qty) as sum_qty, sum(batch_no) as sum_batch_no
from adjustment_stock group by batch_no
) ast on ast.batch_no = ped.id
GROUP BY ped.id;
(Adding up batch numbers seems a bit weird, though.)
You can make separate joins for each table used in subqueries against purchase_entry_details table. In all these separate selects make group by ped.id. Afterwards make left join to new tables with sums.
SELECT
...
SED.sumqty,
SR.sumqty,
...
FROM purchase_entry_details ped
LEFT JOIN products p on p.`product-id` = ped.product_id
LEFT JOIN category c ON c.id = p.`product-type`
LEFT JOIN manufacturer m ON m.id = p.manufacturer
LEFT JOIN (
SELECT ped1.id, SUM(sed1.qty) as sumqty
FROM purchase_entry_details ped1
JOIN sales_entry_details sed1 ON sed1.batch_no = ped1.id
GROUP BY ped1.id
) SED ON SED.id = ped.id
LEFT JOIN (
SELECT ped1.id, SUM(sr1.qty) as sumqty
FROM purchase_entry_details ped1
JOIN sales_return_item sr1 ON sr1.batch_no = ped1.id
GROUP BY ped1.id
) SR ON SR.id = ped.id
...
I have three tables, users(which includes user details), territory_categories(which has different territory names and their ids), user_territory(a junction table between the two)
I want to filter users based on selected territory from the list.
query for that is --
SELECT u.id,account_id_fk,first_name,last_name,email_address,password,mobile_number,gender,user_accuracy,check_in_radius,report_to,role,allow_timeout,
active,last_logged_on,last_known_location_time,last_known_location,u.created_on,u.created_by,
(SELECT COUNT(DISTINCT u2.id) FROM fieldsense.users u2
INNER JOIN user_territory t1 ON u2.id=t1.user_id_fk
INNER JOIN territory_categories c on c.id=t1.teritory_id
**WHERE c.category_name LIKE** "Gujarat" AND(u2.account_id_fk=1 AND u2.role!=0) ) AS usersCount,
IFNULL(a.id,0) attendanceId,IFNULL(a.punch_date,'1111-11-11') punchInDate,IFNULL(a.punch_in,0) punchIntime,IFNULL(a.punch_out_date,'1111-11-11') punchOutDate, IFNULL(a.punch_out,0) punchOutTime
FROM fieldsense.users as u
LEFT OUTER JOIN attendances as a ON u.id=a.user_id_fk AND a.id=(select max(id) from attendances att where att.user_id_fk=u.id)
INNER JOIN user_territory t1 ON u.id=t1.user_id_fk
INNER JOIN territory_categories c on c.id=t1.teritory_id
**WHERE c.category_name LIKE "Gujarat"**
GROUP BY u.id **limit 10**
OUTPUT :
usersCount: 136
attendanceId: 0
punchInDate: 1111-11-11
punchIntime: 0
punchOutDate: 1111-11-11
punchOutTime: 0
10 rows in set (2.06 sec)
And without where clause when it loads it takes almost 1 minute to display the data
OUTPUT without where clause :
usersCount: 144
attendanceId: 0
punchInDate: 1111-11-11
punchIntime: 0
punchOutDate: 1111-11-11
punchOutTime: 0
10 rows in set (54.45 sec)
I am getting the desired output, but query is taking almost 1 minute to load, which i want to optimize. how can i do this ?
Try this out. I just narrowed down your inner join on table user_territory, so it will not join whole user_territory table but will only join distinct user_ids in it. Hope it reduces the performance time,
SELECT u.id,
account_id_fk,
first_name,
last_name,
email_address,
PASSWORD,
mobile_number,
gender,
user_accuracy,
check_in_radius,
report_to,
role,
allow_timeout,
active,
last_logged_on,
last_known_location_time,
last_known_location,
u.created_on,
u.created_by,
(
SELECT
COUNT(u2.id)
FROM
fieldsense.users u2
INNER JOIN (SELECT DISTINCT user_id_fk as id, territory_id FROM user_territory ) t1 ON u2.id = t1.id
INNER JOIN territory_categories c ON c.id = t1.teritory_id
WHERE
c.category_name LIKE "Gujarat"
AND (
u2.account_id_fk = 1
AND u2.role != 0
)
) AS usersCount,
IFNULL(a.id, 0) attendanceId,
IFNULL(a.punch_date, '1111-11-11') punchInDate,
IFNULL(a.punch_in, 0) punchIntime,
IFNULL(
a.punch_out_date,
'1111-11-11'
) punchOutDate,
IFNULL(a.punch_out, 0) punchOutTime
FROM
fieldsense.users AS u
LEFT OUTER JOIN attendances AS a ON u.id = a.user_id_fk
AND a.id = (
SELECT
max(id)
FROM
attendances att
WHERE
att.user_id_fk = u.id
)
INNER JOIN (SELECT DISTINCT user_id_fk as id, territory_id FROM user_territory ) t1 ON u2.id = t1.id
INNER JOIN territory_categories c ON c.id = t1.teritory_id
WHERE
c.category_name LIKE "Gujarat"
GROUP BY
u.id
LIMIT 10
i have 2 sql statements which produce the same result, but wondering which one to choose?
lets say 1 have 3 tables:
supplier
supplier_status
supplier_contact
statement 1)
SELECT a.*, b.status_name
(SELECT c.name FROM contact c
WHERE c.supplier_id = a.supplier_id
ORDER BY c.contact_id DESC limit 1) AS contact_name
FROM supplier a LEFT JOIN supplier_status b
ON a.status_id = b.status_id
statement 2)
SELECT a.*, b.status_name, c.name AS contact_name
FROM supplier a LEFT JOIN supplier_status b
ON a.status_id = b.status_id
LEFT JOIN (SELECT name, supplier_id
FROM contact
ORDER BY contact_id DESC
) c ON a.supplier_id = c.supplier_id
GROUP BY a.supplier_id
Try this query:
SELECT a.*, b.status_name, c.name AS contact_name
FROM supplier a
LEFT JOIN supplier_status b ON a.status_id = b.status_id
LEFT JOIN contact c ON a.supplier_id = c.supplier_id
LEFT JOIN contact d ON c.supplier_id = d.supplier_id AND c.contact_id < d.contact_id
WHERE d.contact_id IS NULL
It's possible that it doesn't produce the same result as yours (I didn't test it) but if it does then all you have to do is to make sure the fields that appear in the JOIN conditions are indexed (they probably are FKs, so they already are).
After 2 days of searching and trying similar questions, it's got to the point where I need to ask the question!
I have the following database structure (simplified)..
mt_product | mt_sku | mt_price
========== | ====== | ========
id | brand_id | mpn | id | product_id | retailer_id | sku | id | sku_id | price | date
For instance...
* A can of Coca-Cola is ONE product.
* It can be sold in many different retailers, who will all have a SKU for it.
* This SKU will have a price, which can change day-by-day.
I want to list the total number of prices for the product.
To list this I currently have the following query which nearly works...
SELECT
p.id AS pid, p.title AS p_title, p.cat, p.mpn,
b.id AS bid, b.name AS brand,
(SELECT COUNT(s.id) FROM mt_sku AS s WHERE s.pid = p.id) AS num_sku,
(SELECT COUNT(gbp.id) FROM mt_price AS gbp INNER JOIN mt_sku ON mt_sku.id = gbp.sid ) AS num_price
FROM mt_product AS p
INNER JOIN mt_brand b ON p.bid = b.id
INNER JOIN mt_sku s ON p.id = s.pid
num_sku returns as expected, however when I introduce the second sub query for num_price (and I have revised this many times) I either get...
* no duplications of the pid but the num_price is the total number of prices to SKUs, not the amount of prices for this product_id (as query above) eg1_img
* the correct number of num_price, but instead of totalling up the total num_price, the pid is duplicated in the table (as query below) - therefore as the pid is duplicated, this does not give me the result I want. I added DISTINCT as it helped an earlier version of the query, it now makes no difference. eg2_img
SELECT
DISTINCT(p.id) AS pid, p.title AS p_title, p.cat, p.mpn,
b.id AS bid, b.name AS brand,
(SELECT COUNT(s.id) FROM mt_sku AS s WHERE s.pid = p.id) AS num_sku,
(SELECT COUNT(gbp.id) FROM mt_price AS gbp WHERE s.id = gbp.sid) AS num_price
FROM mt_product AS p
INNER JOIN mt_brand b ON p.bid = b.id
INNER JOIN mt_sku s ON p.id = s.pid
I'm pretty sure the key to this is that
product can have multiple SKUs, of which a SKU has multiple price history.
Any help or ideas of the schema would be superb.
Try this:
SELECT
p.id AS pid, p.title AS p_title, p.cat, p.mpn,
b.id AS bid, b.name AS brand,
COUNT(DISTINCT s.id) AS num_sku,
COUNT(gbp.id) AS num_price
FROM mt_product AS p
INNER JOIN mt_brand b ON p.brand_id = b.id
INNER JOIN mt_sku s ON p.id = s.product_id
INNER JOIN mt_price gbp ON s.id = gbp.sku_id
GROUP BY b.id, p.id
The products that don't have SKUs defined will not appear in the result set. Use LEFT JOIN mt_sku to make them appear in the result set (having 0 for num_sku and num_price):
LEFT JOIN mt_sku s ON p.id = s.product_id
In both variants of the query, the products that do not have prices defined will not appear in the result set. Use LEFT JOIN mt_price to include them into the result set (having 0 for num_price):
LEFT JOIN mt_price gbp ON s.id = gbp.sku_id
Take a look at the MySQL documentation for JOINs, GROUP BY and GROUP BY aggregate functions.
If you want to list the total prices then you need correlations.
Your first count is fine, because it is correlated to the outer query. The second has no correlation, so that seems strange. The following fixes the num_price subquery:
SELECT p.id AS pid, p.title AS p_title, p.cat, p.mpn,
b.id AS bid, b.name AS brand,
(SELECT COUNT(s2.id) FROM mt_sku s2 WHERE s2.pid = p.id) AS num_sku,
(SELECT COUNT(gbp.id) FROM mt_price gbp WHERE s.id = gbp.sid ) AS num_price
FROM mt_product p INNER JOIN
mt_brand b
ON p.bid = b.id INNER JOIN
mt_sku s
ON p.id = s.pid;
I'm also not sure why you have all the joins in the outer query. I assume that a given product is going to have multiple rows, and you want the multiple rows to have the same num_sku and num_price values.
I have the following MySQL tables (Simplified)
DRIVER (D)
----------
id (PK)
name
RACE (RA)
---------
id (PK)
date
RESULT (RE)
-----------
id (PK)
raceid (FK -> RACE.id)
driverid (FK -> DRIVER.id)
bestRound
averageRound
I want to be able to list all drivers with their numRaces, firstRace, lastRace, bestRound, bestAverageRound and the dates on which their bestRound and bestAverageRound happened. I'm having problems with the last two, the dates for bestRound and bestAverageRound.
This is what I have so far:
SELECT D.name, COUNT(DISTINCT RE.raceid) AS numRaces, min(RA.date) AS firstRace,
max(RA.date) AS lastRace, min(RE.bestRound) AS bestRound,
min(RE.averageRound) AS bestAverageRound
FROM DRIVER D
JOIN RESULT RE ON RE.driverid = D.id
JOIN RACE RA ON RA.id = RE.raceid
GROUP BY D.id
ORDER BY D.name
This is working correctly. But how do I proceed to select the dates from the RACE table on which the bestRound and bestAverageRound occurred? Thanks for your time.
Try this solution:
SELECT
a.*, b.date AS bestRoundDate, c.date AS bestAverageRoundDate
FROM
(
SELECT
d.id,
d.name,
COUNT(DISTINCT re.raceid) AS numRaces,
MIN(ra.date) AS firstRace,
MAX(ra.date) AS lastRace,
MIN(re.bestRound) AS bestRound,
MIN(re.averageRound) AS bestAverageRound
FROM driver d
INNER JOIN result re ON d.id = re.driverid
INNER JOIN race ra ON re.raceid = ra.id
GROUP BY d.id, d.name
) a
INNER JOIN
(
SELECT aa.driverid, aa.bestRound, bb.date
FROM result aa
INNER JOIN race bb ON aa.raceid = bb.id
) b ON a.id = b.driverid AND a.bestRound = b.bestRound
INNER JOIN
(
SELECT aa.driverid, aa.bestAverageRound, bb.date
FROM result aa
INNER JOIN race bb ON aa.raceid = bb.id
) c ON a.id = c.driverid AND a.bestAverageRound = c.bestAverageRound
ORDER BY
a.name