MySQL compare two values on same table - mysql

I'm trying to compare two values in the same table, and check if there is a difference.
Right now, I have 1485 records in the cms_statistics_pages table, but when the query below:
SELECT
cp.identifier,
COUNT(csp1.statID) AS hits,
COUNT(csp2.statID) AS hits_yesterday,
IF(COUNT(csp1.statID)>COUNT(csp2.statID),1,0) AS growth
FROM cms_pages cp
LEFT JOIN cms_statistics_pages csp1
ON csp1.pageID = cp.pageID
AND DATE(csp1.datetime) = '2012-07-20'
LEFT JOIN cms_statistics_pages csp2
ON csp2.pageID = cp.pageID
AND DATE(csp2.datetime) = '2012-07-19'
GROUP BY cp.identifier
..is fired, I get these results:
identifier hits hits_yesterday growth
index 13395 13395 0
siden-er-under-opdatering 638 638 0
vores-historie 0 3 0
Which is not correct for my purpose. Then if I change:
AND DATE(csp1.datetime) = '2012-07-20'
to a date that will match no records
AND DATE(csp1.datetime) = '2012-07-21'
My result now looks like this:
identifier hits hits_yesterday growth
index 0 141 0
siden-er-under-opdatering 0 29 0
vores-historie 0 3 0
Now the hits are correct, so I'm wondering if the query counts the records multiple times when both the joins contains some data.
Example data from cms_pages:
pageID sectionID templateID identifier default title exclude_title
1 1 1 index 1 Welcome to SiteTech Framework 2012
Example data from cms_statistics_pages:
statID frontend backend pageID sectionID panel datetime
1 0 1 34 6 admin 2012-07-17 12:34:14

I came across this post which provides a more advanced way to count across multiple tables, this may prevent the query from counting the same record multiple times. Haven't tried it myself - https://discussion.dreamhost.com/thread-9112.html

So I've been messing around a bit with the query, and found a solution which includes left joins and sub queries. My query now looks like:
SELECT
cp.identifier,
now.hits AS hits,
yd.hits AS hits_yesterday,
IF(now.hits>yd.hits,1,0) AS growth
FROM cms_pages AS cp
LEFT JOIN
(
SELECT
pageID,
COUNT(pageID) AS hits
FROM cms_statistics_pages
WHERE DATE(datetime) = '2012-07-20'
GROUP BY pageID
) now
ON now.pageID = cp.pageID
LEFT JOIN
(
SELECT pageID,
COUNT(pageID) AS hits
FROM cms_statistics_pages
WHERE DATE(datetime) = '2012-07-19'
GROUP BY pageID
) yd
ON yd.pageID = cp.pageID
Which gave me this correct result!:
identifier hits hits_yesterday growth
index 95 141 0
siden-er-under-opdatering 22 29 0
vores-historie NULL 3 0

Related

mySQL Sum Production_Needed Group BY Part_ID

Want to generate a result of Open orders where Production is needed. At issue is each part may have more than one open order. With the GROUP BY my code gives me only one order but does give me the total Production_Needed (which is also a negative in value for orders with enough inventory).
Does my SUM(...) as Production_Needed need to be in the WHERE ?
Thanks,
SELECT part.part_ID AS Part_Part_ID,
part.Inventory, part.part_number,
ord.part_id AS Order_Part_ID,
ord.order_type, ord.quantity_ordered, ord.quantity_shipped,
SUM(ord.quantity_ordered - ord.quantity_shipped - part.Inventory) AS Production_Needed
FROM production_orders ord
JOIN production_part part ON ord.part_ID = part.part_ID
WHERE ord.is_Active = True AND ord.order_type = 0
GROUP BY Order_Part_ID
ORDER BY part.part_number ASC
Data Production_Part part
Part_ID
Part_Inventory
Part_Number
1
12500
97-528
2
0
FC2569
3
1000
39367
Data Production_Orders Ord
Order_Part_ID
Order_Type
Quantity_Ordered
Quantity_Shipped
1
0
8000
0
2
0
1000
500
2
0
1000
0
3
1
10
0
Desired Result - Only Parts that need production
Part_ID
Quantity_Ordered
Quantity_Shipped
2
1000
500
2
1000
0
Untested: need a sampled data set and structure for testing:
This creates an inline view and totals the inventory order amounts then stubtracts it from the inventory to determine if there is a production needed to fulfil open orders. I'd have to use some additional analytical functions if we needed to do this on an order by order basis however; or join these results back into the orders...
--Show parts which lack inventory to fullfill outstanding open orders.
SELECT
P.Part_ID as Part_Part_ID
, P.Inventory
, P.Part_Number
, O.Part_ID as Order_Part_ID
, UnDel_Units-coalesce(P.Inventory,0) as Production_Needed --use coalesce incase no part record exists for some reason.
FROM Production_Part P
RIGHT JOIN ( --use right join just incase part record doesn't exist for some reason
SELECT part_ID, SUM(quantity_ordered-quantity_shipped) as UnDel_Units
FROM PRODUCTION_ORDERS
WHERE IS_ACTIVE=TRUE
and ORDER_TYPE=0
GROUP BY PART_ID) O --derived table "O" for orders showing sum ottal by part of units undelivered
on O.Part_ID=P.Part_ID
WHERE UnDel_Units > coalesce(P.Inventory,0)
-- If inventory is > undelivered units for the part, ignore as additional production isn't needed

MYSQL Query Fill Gaps with Data with LEFT JOIN

I am querying a MYSQL database which has a table named PRICE with the following fields: user, price_date, morning, afternoon. I am grabbing all the morning prices for the current week for the current user to be used as an array in a JS.Charts line chart. The issue I am having is that there may be missing days so the array that I am creating from the query is creating date gaps in the chart.
I created another table named calendar with three fields: datefield, morning, afternoon. I filled it with this years dates (YYYY-MM-DD) along with 0's for the morning and afternoon values.
I now have been trying to write a LEFT JOIN query to select all morning values for the current week and join it with the second table to fill in the date gaps with zeros but I can not get it to work. Any help would be greatly appreciated.
Query:
$sql = "SELECT p.morning, c.morning
FROM price p
LEFT JOIN calendar c ON p.price_date = c.datefield
WHERE p.user = '$user' AND YEARWEEK(p.price_date) = YEARWEEK(NOW())
ORDER BY p.price_date";
The data being used and what is being outputted:
Table Price:
user
price_date
morning
afternoon
lpepper
2021-03-15
23
35
lpepper
2021-03-17
43
52
lpepper
2021-03-18
24
35
lpepper
2021-03-19
78
85
Table Calendar (Partial - goes for whole year)
datefield
morning
afternoon
2021-03-15
0
0
2021-03-16
0
0
2021-03-17
0
0
2021-03-18
0
0
2021-03-19
0
0
2021-03-20
0
0
2021-03-21
0
0
I need the query to return the morning prices for this week (M to SUN) with zeros filled in for the missing dates:
Array should be: 23,0,43,24,78,0,0
My query above returns: ,0,0,0
To make the array I am doing:
$result = mysqli_query($conn, $sql);
$morning = array();
while ($row = mysqli_fetch_assoc($result))
{
$morning[] = $row["morning"];
}
Then when used in the graph:
<?php echo implode(", ", $morning); ?>
In a LEFT JOIN, the table with all the rows that should be in the output should be first.
If you have conditions on the other table, you need to put them in the ON clause. If there's no match to c.datefield, you'll get NULL for the p columns, and testing them in WHERE will filter those rows out.
The date should be filtered from the calendar table, not price. And you should return the date so you know what the rows are for.
Give aliases to p.morning and c.morning so you can distinguish them when getting the columns from the results.
You should order by the column in c, since p.price_date will be NULL for the missing dates.
$sql = "SELECT c.datefield, IFNULL(p.morning, 0) AS price_morning, c.morning AS cal_morning
FROM calendar c
LEFT JOIN price p ON p.price_date = c.datefield
AND p.user = '$user'
WHERE YEARWEEK(c.datefield) = YEARWEEK(NOW())
ORDER BY c.datefield";
DEMO
On an unrelated note, you should use a prepared statement rather than substituting a variable into the SQL.

MySQL Query with LEFT JOIN where second table has a 2-Part Primary Key

I have 2 tables in a MySQL database (storeskus). The first is FBM_Orders and the second is IM_INV.
I am trying the query
SELECT `FBM_Orders`.`order-id`,`FBM_Orders`.`order-item-id`,`FBM_Orders`.`purchase-date`,
`FBM_Orders`.`promise-date`,`FBM_Orders`.`buyer-name`,`FBM_Orders`.`sku`,
`FBM_Orders`.`product-name`,`FBM_Orders`.`quantity-purchased`,
`FBM_Orders`.`recipient-name`,`IM_INV`.`LOC_ID`,`IM_INV`.`QTY_ON_HND`
FROM `FBM_Orders`
LEFT JOIN `IM_INV` ON `FBM_Orders`.`sku` = `IM_INV`.`ITEM_NO`
WHERE `FBM_Orders`.`quantity-to-ship` > 0
ORDER BY `FBM_Orders`.`purchase-date`, `IM_INV`.`LOC_ID` ASC;
Because the IM_INV table has a 2-part primary key: ITEM_NO & LOC_ID, I am getting 4 lines for each ITEM_NO with the QTY_ON_HND for each of the 4 locations (LOC_ID).
I am fairly new to SQL so I'm thrilled to have gotten this far, but how can I make it so that the result is a single line per ITEM_NO but with a column for each LOC_ID with its QTY_ON_HND?
Example:
My current result is
FBM_Order.sku FBM_Order.quantity-purchased IM_INV.LOC_ID QTY_ON_HND
'SCHO645256' 1 AF 2
'SCHO645256' 1 LO 2
'SCHO645256' 1 S 3
'SCHO645256' 1 SL 1
How can I change that to
FBM_Order.sku FBM_Order.quantity-purchased QTY_ON_HND_AF QTY_ON_HND_LO QTY_ON_HND_S QTY_ON_HND_SL
'SCHO645256' 1 2 2 3 1
?
Thanks!
You may load it as you already do and treat it inside your application, but if you really wanna make that inside your MySQL, try GROUP CONCAT and JSON as follows:
SELECT
GROUP_CONCAT(JSON_OBJECT(
'LOC_ID', IM_INV.LOC_ID,
'QTY_ON_HND', QTY_ON_HND
))
{another fields}
FROM `FBM_Orders`
LEFT JOIN `IM_INV` ON `FBM_Orders`.`sku` = `IM_INV`.`ITEM_NO`
WHERE `FBM_Orders`.`quantity-to-ship` > 0
GROUP BY `FBM_Orders`.`order-id`;
Note: JSON is just available for MySQL 5.7+ and may slow down your query a little bit. You're still gonna need convert your data to array inside your application. So it's half done inside your app and half inside your database.

query optimization for mysql

I have the following query which takes about 28 seconds on my machine. I would like to optimize it and know if there is any way to make it faster by creating some indexes.
select rr1.person_id as person_id, rr1.t1_value, rr2.t0_value
from (select r1.person_id, avg(r1.avg_normalized_value1) as t1_value
from (select ma1.person_id, mn1.store_name, avg(mn1.normalized_value) as avg_normalized_value1
from matrix_report1 ma1, matrix_normalized_notes mn1
where ma1.final_value = 1
and (mn1.normalized_value != 0.2
and mn1.normalized_value != 0.0 )
and ma1.user_id = mn1.user_id
and ma1.request_id = mn1.request_id
and ma1.request_id = 4 group by ma1.person_id, mn1.store_name) r1
group by r1.person_id) rr1
,(select r2.person_id, avg(r2.avg_normalized_value) as t0_value
from (select ma.person_id, mn.store_name, avg(mn.normalized_value) as avg_normalized_value
from matrix_report1 ma, matrix_normalized_notes mn
where ma.final_value = 0 and (mn.normalized_value != 0.2 and mn.normalized_value != 0.0 )
and ma.user_id = mn.user_id
and ma.request_id = mn.request_id
and ma.request_id = 4
group by ma.person_id, mn.store_name) r2
group by r2.person_id) rr2
where rr1.person_id = rr2.person_id
Basically, it aggregates data depending on the request_id and final_value (0 or 1). Is there a way to simplify it for optimization? And it would be nice to know which columns should be indexed. I created an index on user_id and request_id, but it doesn't help much.
There are about 4907424 rows on matrix_report1 and 335740 rows on matrix_normalized_notes table. These tables will grow as we have more requests.
First, the others are right about knowing better how to format your samples. Also, trying to explain in plain language what you are trying to do is also a benefit. With sample data and sample result expectations is even better.
However, that said, I think it can be significantly simplified. Your queries are almost completely identical with the exception of the one field of "final_value" = 1 or 0 respectively. Since each query will result in 1 record per "person_id", you can just do the average based on a CASE/WHEN AND remove the rest.
To help optimize the query, your matrix_report1 table should have an index on ( request_id, final_value, user_id ). Your matrix_normalized_notes table should have an index on ( request_id, user_id, store_name, normalized_value ).
Since your outer query is doing the average based on an per stores averages, you do need to keep it nested. The following should help.
SELECT
r1.person_id,
avg(r1.ANV1) as t1_value,
avg(r1.ANV0) as t0_value
from
( select
ma1.person_id,
mn1.store_name,
avg( case when ma1.final_value = 1
then mn1.normalized_value end ) as ANV1,
avg( case when ma1.final_value = 0
then mn1.normalized_value end ) as ANV0
from
matrix_report1 ma1
JOIN matrix_normalized_notes mn1
ON ma1.request_id = mn1.request_id
AND ma1.user_id = mn1.user_id
AND NOT mn1.normalized_value in ( 0.0, 0.2 )
where
ma1.request_id = 4
AND ma1.final_Value in ( 0, 1 )
group by
ma1.person_id,
mn1.store_name) r1
group by
r1.person_id
Notice the inner query is pulling all transactions for the final value as either a zero OR one. But then, the AVG is based on a case/when of the respective value for the normalized value. When the condition is NOT the 1 or 0 respectively, the result is NULL and is thus not considered when the average is computed.
So at this point, it is grouped on a per-person basis already with each store and Avg1 and Avg0 already set. Now, roll these values up directly per person regardless of the store. Again, NULL values should not be considered as part of the average computation. So, if Store "A" doesn't have a value in the Avg1, it should not skew the results. Similarly if Store "B" doesnt have a value in Avg0 result.

mysql query if condition

Hi there i have two tables a2_deal(I havent mentioned entire table as its very big)
deviceID companyID stage serverTime
1 14 -1 1349449200
1 1 -1 1349445600
2 21 -1 1349449200
3 17 -1 1349447160
1 14 3 1344449200
1 14 2 1340449200
and another table called a2_comp
companyID name
1 Microsoft
14 DELL
15 APPLE
17 Google
I am trying to get the most recent stage of a company By using below query:
SELECT deal.companyID, companies.name as Company,
if(max(serverTime),stage,Null) as Stage
FROM `a2_deal` AS deal
LEFT JOIN `a2_comp` AS companies ON deal.companyID = companies.companyID
GROUP BY companyID
ORDER BY serverTime
in my query i am using if(max(serverTime),stage,Null) as Stage which means select the stage value related to most recent server time . ie it should give me -1 as the stage of companyID 14.... But for some reason i am not getting correct output..Please explain how my logic is wrong here... Thank You
You want the groupwise maximum:
SELECT a2_comp.*, a2_deal.*
FROM a2_deal NATURAL JOIN (
SELECT companyID, MAX(serverTime) AS serverTime
FROM a2_deal
GROUP BY companyID
) t JOIN a2_comp USING (companyID)
See it on sqlfiddle.
case is used for inline conditions in your query. Also, you may need to do
(case when max(serverTime) = serverTime then stage else null end) as Stage
I'm not totally sure that's valid, but you can try it out.
Try this
SELECT deal.companyID, deal.stage, comp.name
FROM a2_deal AS deal, a2_comp AS comp
WHERE deal.serverTime =
(SELECT MAX(deal2.serverTime)
FROM a2_deal AS deal2
WHERE deal2.companyID = deal.companyID)
AND comp.companyID = deal.companyID
GROUP BY deal.companyID
This might be a little confusing but the most interesting part is the sub query which selecting recent serverTime for each company. I have used theta style query and hence JOIN is not necessary.