MySQL UNION ALL Derived Table with GROUP BY Not Working - mysql

I've been scratching my head on this for days! I made the query below to combine two subqueries and group them by date. The first query in the derived table returns 50 rows, while the second query returns 2 rows.
But if I run the whole thing, it only returns the 50 rows from the FIRST query.
AND whatever should have been combined from the second query IS NOT combined at all to the rows with the same GROUP BY column value.
SELECT tot.payment_date AS "Payment Period",
tot.total_sales AS "Total Sales"
FROM
(
SELECT DATE_FORMAT(a.payment_dt, "%c/%d/%Y") AS payment_date,
CAST((SUM(b.ucost * b.qty) + a.shipping_fee) AS DECIMAL(15,2)) AS total_sales
FROM tbl_encash_order_sum a
INNER JOIN tbl_encash_order_det b
ON a.accid = b.accid AND a.so_no = b.so_no
WHERE a.payment_stat = 1
GROUP BY DATE_FORMAT(a.payment_dt, "%c/%d/%Y")
UNION ALL
SELECT DATE_FORMAT(d.dp_settled_dt, "%c/%d/%Y") AS payment_date,
SUM(d.order_total) AS total_sales
FROM wp_posts c
INNER JOIN
(
SELECT post_id,
MAX(CASE WHEN meta_key = "_payment_status" THEN CAST(meta_value AS SIGNED) END) AS payment_status,
MAX(CASE WHEN meta_key = "_order_total" THEN CAST(meta_value AS DECIMAL(15,2)) END) AS order_total,
MAX(CASE WHEN meta_key = "_dp_settled_dt" THEN CAST(meta_value AS DATETIME) END) AS dp_settled_dt
FROM wp_postmeta
GROUP BY post_id
) d
ON c.ID = d.post_id
WHERE c.post_type = "shop_order" AND d.payment_status = 1
GROUP BY DATE_FORMAT(d.dp_settled_dt, "%c/%d/%Y")
) tot
GROUP BY tot.payment_date
ORDER BY tot.payment_date

Here:
SELECT tot.payment_date AS "Payment Period",
tot.total_sales AS "Total Sales"
you should be doing SUM(tot.total_sales). Without the sum, it will return an arbitrary one of the total sales for each payment date.
You can make mysql give an error instead of choosing arbitrary data to return by enabling the ONLY_FULL_GROUP_BY sqlmode.

Related

MySQL upgrade from left join to something similar as FULL join

Sorry for asking here this but I need help and google is not being nice.
I have the following table Products
SELECT
COUNT(CASE when core.kits.Location = core.suppliers.id THEN 1 END) as total,
COUNT(CASE when core.kits.cp = 1 THEN 1 END) as used,
core.suppliers.id, core.suppliers.name, core.suppliers.email,
core.suppliers.cperson, core.suppliers.adress, core.suppliers.phone
FROM core.kits
LEFT join core.suppliers on core.kits.Location = core.suppliers.id
WHERE core.suppliers.id is not null
AND banned=0
GROUP BY core.suppliers.id
ORDER BY name ASC
LIMIT 1000 OFFSET 0
but does not give me all the suppliers with zeros for the ones who have no appearance in kits.
Then in I do
SELECT
COUNT(CASE when core.kits.Location = core.suppliers.id THEN 1 END) as total,
COUNT(CASE when core.kits.cp = 1 THEN 1 END) as used,
core.suppliers.id, core.suppliers.name, core.suppliers.email,
core.suppliers.cperson, core.suppliers.adress, core.suppliers.phone
FROM core.suppliers
LEFT join core.suppliers on core.suppliers.id = core.kits.Location
WHERE core.suppliers.id is not null
AND banned=0
GROUP BY core.suppliers.id
ORDER BY name ASC
LIMIT 1000 OFFSET 0
I get all suppliers and correct numbers but the query takes 8 seconds instead of 1s. Any ideas how can I get all the suppliers with the count of stocks in 1s?
cheers.
If you want all the suppliers, even those that do not appear in kits you should do a LEFT join of suppliers to kits:
SELECT COUNT(k.Location) AS total,
COUNT(CASE WHEN k.cp = 1 THEN 1 END) AS used,
s.id, s.name, s.email, s.cperson, s.adress, s.phone
FROM core.suppliers s LEFT JOIN core.kits k
ON k.Location = s.id
WHERE banned=0
GROUP BY s.id
ORDER BY s.name ASC
LIMIT 1000 OFFSET 0;
I assume that core.suppliers.id is the primary key of suppliers, so that the conition:
core.suppliers.id is not null
is not needed.
Also, if the column banned is contained in the table kits, then the condition should be moved in the ON clause:
ON k.Location = s.id AND k.banned=0
and the WHERE clause should be removed.

How to get different status count by product id

I have got the result form a complex query below
SELECT o_items.sku,
o_items.name AS 'title',
o_items.qty_ordered AS 'quantity',
s_orders.base_amount_paid AS 'paid/unpaid'
FROM sales_order_payment s_orders
INNER JOIN (SELECT s.sku, s.name, s.qty_ordered, s.order_id
FROM sales_order_item s
INNER JOIN (SELECT p.entity_id
FROM catalog_product_entity AS p
INNER JOIN catalog_product_entity_int AS a
ON p.row_id = a.row_id
WHERE VALUE >= 0
AND
a.attribute_id =
(SELECT attribute_id
FROM eav_attribute
WHERE attribute_code = 'is_darkhorse')) as q
ON s.product_id = q.entity_id
WHERE s.created_at BETWEEN '2019-01-14' AND '2019-01-16') o_items
ON
s_orders.parent_id = o_items.order_id
this is the order data those have been paid or not paid yet. Amount is representing paid and Null representing unpaid status
I am trying to generate below result but couldn't succeed and need help. Actually this result is showing how may quantity of a product has been paid and how many not paid yet. This would be result of above fetched data.
Please guide me how can i proceed to achieve these result.
Use this. ... represent existing code.
select .... , sum(case when s_orders.base_amount_paid is not null
then o_items.qty_ordered
else 0
end) as paid,
sum(case when s_orders.base_amount_paid is null
then o_items.qty_ordered
else 0
end) as unpaid
From .......
You can use if and ifnull functions together(presuming you're using mysql as DBMS)
and GROUP BY expression
SELECT c.sku, c.name,
sum(if(ifnull(base_amount_paid,0)=0,0,1)) as paid,
sum(if(ifnull(qty_ordered,0)=0,0,1)) as unpaid
FROM catalog_prod_ent_derived c
GROUP BY c.sku, c.name
where catalog_prod_ent_derived represents your whole query as a subquery.

Get only 1 result per group of ID

I have a list of records (domestic_helper_idcards) and I want to return only one card per staff (domestic_helper_id) that is not deleted (is_deleted = 0), and that has the card_expiration_date furthest in the future (latest expiry date).
Have tried grouping and so on, but cant get it to work. Code below:
SELECT * FROM domestic_helper_idcard
where
is_deleted = 0
order by card_expiration_date desc
This returns the following (image):
I want only records with ID 4 and 5 to be returned. Anyone?
You could use a join with the subquery grouped by domestic_helper_id with an aggregated function eg: max()
SELECT d.*
FROM domestic_helper_idcard d
inner join (
select domestic_helper_id, max(id) max_id
from domestic_helper_idcard
where is_deleted = 0
group by domestic_helper_id
) t on t.domestic_helper_id = d.domestic_helper_id and t.max_id = d.id
order by d.card_expiration_date desc
and as suggested by Jens after clarification using max card_expiration_date
SELECT d.*
FROM domestic_helper_idcard d
inner join (
select domestic_helper_id, max(card_expiration_date) max_date
from domestic_helper_idcard
where is_deleted = 0
group by domestic_helper_id
) t on t.domestic_helper_id = d.domestic_helper_id and t.max_date = d.max_date
order by d.card_expiration_date desc

shows mysql records twice because of inner joining

In below query (Mentors) are 13 which shows me 26, while (SchoolSupervisor) are 5 which shows me 10 which is wrong. it is because of the Evidence which having 2 evidance, because of 2 evidence the Mentors & SchoolSupervisor values shows me double.
please help me out.
Query:
select t.c_id,t.province,t.district,t.cohort,t.duration,t.venue,t.v_date,t.review_level, t.activity,
SUM(CASE WHEN pr.p_association = "Mentor" THEN 1 ELSE 0 END) as Mentor,
SUM(CASE WHEN pr.p_association = "School Supervisor" THEN 1 ELSE 0 END) as SchoolSupervisor,
(CASE WHEN count(file_id) > 0 THEN "Yes" ELSE "No" END) as evidence
FROM review_m t , review_attndnce ra
LEFT JOIN participant_registration AS pr ON pr.p_id = ra.p_id
LEFT JOIN review_files AS rf ON rf.training_id = ra.c_id
WHERE 1=1 AND t.c_id = ra.c_id
group by t.c_id, ra.c_id order by t.c_id desc
enter image description here
You may perform the aggregations in a separate subquery, and then join to it:
SELECT
t.c_id,
t.province,
t.district,
t.cohort,
t.duration,
t.venue,
t.v_date,
t.review_level,
t.activity,
pr.Mentor,
pr.SchoolSupervisor,
rf.evidence
FROM review_m t
INNER JOIN review_attndnce ra
ON t.c_id = ra.c_id
LEFT JOIN
(
SELECT
p_id,
COUNT(CASE WHEN p_association = 'Mentor' THEN 1 END) AS Mentor,
COUNT(CASE WHEN p_association = 'School Supervisor' THEN 1 END) AS SchoolSupervisor,
FROM participant_registration
GROUP BY p_id
) pr
ON pr.p_id = ra.p_id
LEFT JOIN
(
SELECT
training_id,
CASE WHEN COUNT(file_id) > 0 THEN 'Yes' ELSE 'No' END AS evidence
FROM review_files
GROUP BY training_id
) rf
ON rf.training_id = ra.c_id
ORDER BY
t.c_id DESC;
Note that this also fixes another problem your query had, which was that you were selecting many columns which did not appear in the GROUP BY clause. Under this refactor, there is nothing wrong with your current select, because the aggregation take place in a separate subquery.
try adding this to the WHERE part of your query
AND pr.p_id IS NOT NULL AND rf.training_id IS NOT NULL
You can add a group by pr.p_id to remove the duplicate records there. Since, the group by on pr is not present as of now, there might be multiple records of same p_id for same ra
group by t.c_id, ra.c_id, pr.p_id order by t.c_id desc

how to get last record of table in sql

I have three entries in the attendance table.But I need to print the last entry of the id from the attendance table.I calculated total duration in separate query and printing list in separate query.Anyone please help me to resolve this.
query to print total duration:
select SEC_TO_TIME(sum(case when a.endtime IS NULL
then time_to_sec(TIMEDIFF(NOW(),a.starttime))
else time_to_sec(a.duration)
end)
) as duration
from attendance a
left join staff s on a.staffid=s.id
left join company c on a.companyid=c.companyid
where DATE_FORMAT(a.createdon, '%Y/%m/%d') = DATE_FORMAT('2017-04-04', '%Y/%m/%d') and
(a.employeecode ='A101' OR 'A101'='') and
(a.companyid=0 OR 0=0)
group by a.employeecode,a.companyid order by a.id desc
Query to print the details:
select a.id,
a.starttime,
a.endtime,
a.startlocation,
a.endlocation,
a.duration,
a.companyid,
a.employeecode,
a.staffid,
a.createdon,
a.createdby,
a.lastmodifiedon,
a.reason,
a.comments,
s.name as staffName,
c.company_name as companyName
from attendance a
left joinstaff s on a.staffid=s.id
left join company c on a.companyid=c.companyid
where DATE_FORMAT(a.created_on, '%Y/%m/%d') = DATE_FORMAT('2017-04-04', '%Y/%m/%d') and
(a.employeecode ='A101' OR 'A101'='') and
(a.company_id=0 OR 0=0)
group by a.employeecode,a.companyid order by a.id desc
I got output by setting the endtime as null.
`select SEC_TO_TIME(sum(case when a.endtime IS NULL then time_to_sec(TIMEDIFF(NOW(),a.starttime)) else time_to_sec(a.duration) end))
from attendance a left join staff s on a.staffid=s.id
left join company c on a.companyid=c.companyid
where DATE_FORMAT(a.createdon, '%Y/%m/%d') = DATE_FORMAT('', '%Y/%m/%d') and
(a.employeecode ='' OR ''='') and
(a.companyid=0 OR 0=0)
group by a.employeecode,a.companyid order by a.id desc`