Having a structure of 3 tables
Table min consist of matcode,min_qty,jo_no,mr_no
Table min_out_body consist of matcode,out_qty,jo_no,mr_no
Table eu_min_out_body consist of matcode,out_qty,jo_no,mr_no
And data as follow:
[min]
matcode min_qty jo_no mr_no
xxx 100 1A A11
xxx 150 2A A22
[min_out_body]
matcode out_qty jo_no mr_no
xxx 10 1A A11
xxx 60 1A A11
xxx 100 2A A22
[eu_min_out_body]
matcode out_qty jo_no mr_no
xxx 20 1A A11
xxx 50 2A A22
What i am trying to achieve is to have a result:
matcode min_qty jo_no mr_no balance
xxx 100 1A A11 10
xxx 150 2A A22 0
Queried using following code :
SELECT
min.matcode,
min.min_qty,
min.jo_no,
min.mr_no
(min.min_qty-(
select ifnull(sum(out_qty),0)
FROM min_out_body
WHERE matcode=min.matcode
and jo_no=min.jo_no
and mr_no=min.mr_no
)-(
select ifnull(sum(out_qty),0)
FROM eu_min_out_body
WHERE matcode=min.matcode
and jo_no=min.jo_no
and mr_no=min.mr_no
)
) as balance
FROM min
WHERE min.matcode = 'xxx'
and (min.min_qty - (select
ifnull(sum(out_qty),0)
FROM min_out_body
WHERE matcode = min.matcode
and jo_no = min.jo_no
and mr_no = min.mr_no) - (select
ifnull(sum(out_qty),0)
FROM eu_min_out_body
WHERE matcode = min.matcode
and jo_no = min.jo_no
and mr_no = min.mr_no)) > 0
I can get the result, but is there any way to simplify the query and reduce the process time?
I see two options, and depending on the amount of data you are dealing with could perform better than other... The first is easier readability wise but simplifies your WHERE clause and may be what you want to run with..
Wrap your query but only up to the WHERE MAT_CODE qualifier. Let that "PQ" (PreQuery) return everything -- ALL balances. Then from that, just apply WHERE balance > 0 vs your complex balance calculation completed AGAIN in the where clause.
SELECT PQ.*
from
( SELECT
min.matcode,
min.min_qty,
min.jo_no,
min.mr_no
(min.min_qty
- ( select ifnull(sum(out_qty),0)
FROM min_out_body
WHERE min_no = min.min_no
and matcode = min.matcode
and jo_no = min.jo_no
and mr_no = min.mr_no )
- ( select ifnull(sum(out_qty),0)
FROM eu_min_out_body
WHERE min_no=min.min_no
and matcode=min.matcode
and jo_no=min.jo_no
and mr_no=min.mr_no ) ) as balance
FROM
min
WHERE
min.matcode = 'xxx' ) PQ
where
balance > 0
If a larger data set, It looks a little larger since it does pre-aggregate queries ONCE per the "out_body" tables and groups by the components your inner field selects were dealing with. Then LEFT JOIN to those tables. The benefit of this approach is that it doesn't run the inner select sum() on a per record basis, and if you have 1000's of records to go through, might be a bigger time saver for you... especially if its going through MIN_OUT_BODY and EU_MIN_OUT_BODY.
By Left-Join, it's either their or not, and the IFNULL() works the same way, then the WHERE clause is the same simple readable computation of the balance WITHOUT having to requery yet again.
SELECT
min.matcode,
min.min_qty,
min.jo_no,
min.mr_no,
min.min_qty - ifnull( MOB1.out_qty, 0 ) - ifnull( EMOB1.out_qty, 0 ) balance
from
min
LEFT JOIN ( select
mob.matcode,
mob.min_no,
mob.jo_no,
mob.mr_no,
SUM( mob.out_qty ) out_qty
from
min_out_body mob
where
mob.matcode = 'xxx'
group by
mob.matcode,
mob.min_no,
mob.jo_no,
mob.mr_no ) MOB1
ON min.matcode = MOB1.matcode
AND min.min_no = MOB1.min_no
AND min.jo_no = MOB1.jo_no
AND min.mr_no = MOB1.mr_no
LEFT JOIN ( select
Emob.matcode,
Emob.min_no,
Emob.jo_no,
Emob.mr_no,
SUM( Emob.out_qty ) out_qty
from
eu_min_out_body Emob
where
Emob.matcode = 'xxx'
group by
Emob.matcode,
Emob.min_no,
Emob.jo_no,
Emob.mr_no ) EMOB1
ON min.matcode = EMOB1.matcode
AND min.min_no = EMOB1.min_no
AND min.jo_no = EMOB1.jo_no
AND min.mr_no = EMOB1.mr_no
where
min.matcode = 'xxx'
AND min.min_qty - ifnull( MOB1.out_qty, 0 ) - ifnull( EMOB1.out_qty, 0 ) > 0
Related
I'm trying to get data that have the same medicine_id and unique insurance_id and last inserted row. Put Group by and Order by but in that got random data not last inserted.
I tried this code but got not last inserted data
SELECT
`m1`.`*`
FROM
(
`pricings` `m1`
LEFT JOIN `pricings` `m2` ON
(
(
(
`m1`.`medicine_id` = `m2`.`medicine_id`
)
)
)
)
WHERE m1.medicine_id = 2
group BY m1.insurance_id DESC
ORDER BY m1.created_at;
Here are the total rows.
This is a full table
id
medicine_id
insurance_id
created_at
4311
2
1
2021-04-12 16:05:07
4766
2
1
2022-01-15 11:56:06
4767
2
38
2021-05-12 08:17:11
7177
2
38
2022-03-30 10:14:11
4313
2
39
2021-04-12 16:05:46
4768
2
39
2021-05-12 08:17:30
1356
2
40
2020-11-02 11:25:43
3764
2
40
2021-03-08 15:42:16
4769
2
40
2021-05-12 08:17:44
And I want to like this
id
medicine_id
insurance_id
created_at
4766
2
1
2022-01-15 11:56:06
4768
2
39
2021-05-12 08:17:30
4769
2
40
2021-05-12 08:17:44
7177
2
38
2022-03-30 10:14:11
MySQL 5.x: Use a sub-query to find the max created_at value per group, then join that on the source table to identify the row it was from.
SELECT
p.`*`
FROM
`pricings` p
INNER JOIN
(
SELECT
`medicine_id`,
`insurance_id`,
MAX(created_at) AS `created_at`
FROM
`pricings`
GROUP BY
`medicine_id`,
`insurance_id`
)
p_max
ON p.`medicine_id` = p_max.`medicine_id`
AND p.`insurance_id` = p_max.`insurance_id`
AND p.`created_at` = p_max.`created_at`
WHERE
p.`medicine_id` = 2
ORDER BY
p.`created_at`;
MySQL 8: Use ROW_NUMBER() to enumerate each group, then pick the first row from each group.
SELECT
p.`*`
FROM
`pricings` p
FROM
(
SELECT
*,
ROW_NUMBER() OVER (
PARTITION BY `medicine_id`,
`insurance_id`
ORDER BY `created_at` DESC
)
AS `row_id`
FROM
`pricings`
)
p
WHERE
p.`medicine_id` = 2
AND p.`row_id` = 1
ORDER BY
p.`created_at`;
Adding it as an answer as well. I have not tested it, just fix the formating to work with whatever version of databse you are working with and let me know of the results.
SELECT m1.id , m1.Insurance_id , m1.medicine_id , max(m1,created_at)
FROM (
`pricings` `m1` LEFT JOIN `pricings` `m2` ON `m1`.`medicine_id` = `m2`.`medicine_id`
)
WHERE m1.medicine_id = 2 and m1.insurance_id in (1,39,40,38)
GROUP BY m1.insurance_id DESC
ORDER BY m1.created_at;
Edit. I also removed the 6 extra parenthesis, I don't see how they could be of any use
I am new with mysql and working to change a store application to make it have two stock. I created a table to store stock quantity:
Then I plan to create a view with stock quantity, per store, per SKU. I using the following query:
SELECT
`stockList`.`sku`,
SUM(A.`stockQty`) AS 'store1',
SUM(B.`stockQty`) AS 'store2',
SUM(`stockList`.`stockQty`) AS 'total'
FROM `stockList`
LEFT JOIN (
SELECT * FROM `stockList` WHERE `idStock`=1
) AS A
ON `stockList`.`sku`=A.`sku`
LEFT JOIN (
SELECT * FROM `stockList` WHERE `idStock`=2
) AS B
ON `stockList`.`sku`=B.`sku`
GROUP BY `stockList`.`sku`
Per resulting table, calculation is not proper and I could not identify the logic:
SKU 43 should show for store1 = 9 and for store2 = 10, total = 19. This is what they show if I execute the select queries alone. Please, let me know if I misunderstood how this sum logic works.
You might to use SUM on subquery to calculate Totle price by sku
LEFT JOIN may make some fields not match causing NULL so use IFNULL to preset value 0
You can try this.
SELECT
T.sku,
SUM(T.stockQty) as totle,
IFNULL(A.`store1`,0) AS `store1`,
IFNULL(B.`store2`,0) AS `store2`
FROM `stockList` AS T
LEFT JOIN
(
SELECT sku,SUM(`stockQty`) as `store1`
FROM `stockList`
WHERE `idStock`=1
GROUP BY sku
) as A ON A.sku = T.sku
LEFT JOIN
(
SELECT sku,SUM(`stockQty`) as `store2`
FROM `stockList`
WHERE `idStock`=2
GROUP BY sku
) AS B ON T.sku =B.sku
GROUP BY T.sku
sqlfiddle
Your query is much more complicated than it needs to be. You can just do this:
SELECT
sku,
SUM(stockQty) as total,
SUM(IF(idStock=1,stockQty,0)) AS `store1`,
SUM(IF(idStock=2,stockQty,0)) AS `store2`
FROM `stockList`
GROUP BY sku
Output:
sku total store1 store2
36 10 10 0
37 3 3 0
38 4 4 0
39 3 3 0
40 10 10 0
41 12 12 0
42 12 12 0
43 19 9 10
I am trying to run a query to select information from a table where conditions on two others are met based on time and one other condition.
The tables are:
Lead
lead_id lead_bname lead_tname lead_created
1 ABC Stores ABC 2015-06-01 12:20:50
2 DEF Hutt DEF 2015-07-13 13:52:06
Lead Owner
lead_owner_id lead_id staff_id lead_owner_timestamp
1 1 105 2015-06-01 12:20:50
2 1 103 2015-06-01 12:20:51
3 2 105 2015-07-13 13:52:06
Lead Status
lead_status_id lead_id status_ID lead_status_timestamp
1 1 54 2015-06-01 12:20:50
2 1 56 2015-06-06 10:14:55
3 2 54 2015-07-13 13:52:06
With a query I need to get the lead_bname and lead_tname where the timestamps (not created) are with a dynamic timeframe. (e.g. 2015-06-01 -> 2015-06-03) and then matches to a particular staff_id at that time even if earlier or later it has changed. The same again for the status.
I attempted the below, but I got a 0 result.
SELECT l.lead_id, lead_bname, lead_tname, lead_created
FROM lead l, lead_owner o, lead_status s
WHERE lead_owner_timestamp = (
SELECT max(lead_owner_timestamp)
FROM lead_owner
WHERE lead_ID = l.lead_ID)
AND ( lead_owner_timestamp <= '2015-06-01' AND lead_owner_timestamp >= '2015-06-03')
AND staff_id = '103'
AND lead_status_timestamp = (
SELECT max(lead_status_timestamp)
FROM lead_status WHERE lead_ID = l.lead_ID)
AND status_ID = '54'
AND ( lead_status_timestamp <= '2015-06-01' AND lead_status_timestamp >= '2015-06-03')
Expected Result
lead_bname lead_tname lead_created status_id
ABC Stores ABC 2015-06-01 12:20:50 54
This is because at between these two dates the lead was created and the status set at '54' and the staff_id was assigned as '103' as the latest entry.
If the staff was selected as '105' I would expect to receive a 0 result, which is still useful to me.
Really stuck on this one, imagining that I will require some Inner or Outer Joins on this, but it's a little beyond me, so hoping someone here can help out.
You're right, you need joins and some inner selects. Try this:
SELECT l.lead_bname, l.lead_tname, l.lead_created, ls.status_id
FROM lead l
INNER JOIN
(SELECT lead_owner_id, lead_id, staff_id, lead_owner_timestamp
FROM lead_owner
WHERE lead_owner_timestamp = (SELECT MAX(lead_owner_timestamp) FROM lead_owner WHERE staff_id = 103)
AND lead_owner_timestamp >= '2015-06-01'
AND lead_owner_timestamp <= '2015-06-03'
AND staff_id = 103
) lo ON l.lead_id = lo.lead_id
INNER JOIN
(SELECT lead_status_id, lead_id, status_id, lead_status_timestamp
FROM lead_status
WHERE lead_status_timestamp >= '2015-06-01'
AND lead_status_timestamp <= '2015-06-03'
AND status_id = 54
) ls ON l.lead_id = ls.lead_id
Notice that with this new revision there are two places that you need to change the staff id when necessary - line 6 WHERE staff_id = 103 and line 9 AND staff_id = 103. I created a test database with all the data you gave in it and it return the row you expected when the staff id is 103 and nothing when it is 105.
When you are check date only part must be convert datetime to date like this
SELECT l.lead_bname, l.lead_tname, l.lead_created, ls.status_id
FROM lead l
INNER JOIN lead_owner lo ON l.lead_id = lo.lead_id
INNER JOIN lead_status ls ON l.lead_id = ls.lead_id
WHERE (
CONVERT(lo.lead_owner_timestamp,DATE) <= '2015-06-01'
AND CONVERT(lo.lead_owner_timestamp,DATE) >= '2015-06-03'
)
AND (
CONVERT(ls.lead_status_timestamp,DATE) <= '2015-06-01'
AND CONVERT(ls.lead_status_timestamp,DATE) >= '2015-06-03'
)
AND ls.status_id = 54
Table 1:
Date PlacementID CampaignID Impressions
04/01/2014 100 10 1000
04/01/2014 101 10 1500
04/01/2014 100 11 500
Table 2:
Date PlacementID CampaignID Cost
04/01/2014 100 10 5000
04/01/2014 101 10 6000
04/01/2014 100 11 7000
04/01/2014 103 10 8000
When I have joined this table using Full Join and Left Join statement, I am not able to get uncommon record which is last row in table2 that display PlacementID 103 and campaignID 10 and Cost 8000. However I have searched all raw data and file but this missing records are not common between two sources. However, I want to include this records in final table. How can I do that? This two table are two different source and I have got results only common records.
Moreover, when I found out that missing value is exact value that are required in final figure so want to include every thing. I am including my SQL script below:
SELECT A.palcementid,
A.campaignid,
A.date,
Sum(A.impressions) AS Impressions,
Sum(CASE
WHEN C.placement_count > 1 THEN ( B.cost / C.placement_count )
ELSE B.cost
END) AS Cost
FROM table1 A
FULL JOIN table2 B
ON A.placementid = B.placementid
AND A.campaignid = B.campaignid
AND A.date = B.date
LEFT JOIN (SELECT Count(A.placementid) AS Placement_Count,
placementid. campaignid,
date
FROM table1
GROUP BY placementid,
campaignid,
date) c
ON A.placementid = C.placementid
AND A.campaignid = C.campaignid
AND A.date = C.date
GROUP BY A.placementid,
A.campaignid,
A.date
I am dividing Cost by placement because in source the cost was allocated for one placement only and one time so I have to divide those because in actual table the same Placementid repeat more than 1 times on same date.
As you didn't provide any expected output I guessing here but if the result you want is this:
PlacementID CampaignID Date Impressions Cost
----------- ----------- ----------------------- ----------- -----------
100 10 2014-04-01 02:00:00.000 1000 5000
100 11 2014-04-01 02:00:00.000 500 7000
101 10 2014-04-01 02:00:00.000 1500 6000
103 10 2014-04-01 02:00:00.000 NULL 8000
Then the following query should do it:
SELECT COALESCE(A.PlacementID,b.placementid) AS PlacementID,
COALESCE(A.campaignid, b.campaignid) AS CampaignID,
COALESCE(A.date, b.date) AS [Date],
SUM(A.impressions) AS Impressions,
SUM(CASE
WHEN C.placement_count > 1 THEN ( B.cost / C.placement_count )
ELSE B.cost
END ) AS Cost
FROM table1 A
FULL JOIN table2 B
ON A.[PlacementID] = B.placementid
AND A.campaignid = B.campaignid
AND A.date = B.date
LEFT JOIN (SELECT COUNT(PlacementID) AS Placement_Count,
placementid, campaignid,
date
FROM table1
GROUP BY placementid,
campaignid,
date) c
ON A.[PlacementID] = C.placementid
AND A.campaignid = C.campaignid
AND A.date = C.date
GROUP BY COALESCE(A.PlacementID, B.PlacementID),
COALESCE(A.campaignid, b.campaignid),
COALESCE(A.date, b.date)
Sample SQL Fiddle
SELECT s1.ID FROM binventory_ostemp s1 JOIN
( SELECT Cust_FkId, ProcessID, MAX(Service_Duration) AS duration
FROM binventory_ostemp WHERE ProcessID='4d2d6068678bc' AND Overall_Rank IN
(
SELECT MIN(Overall_Rank) FROM binventory_ostemp WHERE ProcessID='4d2d6068678bc' GROUP BY Cust_FkId
)
GROUP BY Cust_FkId
) AS s2 ON s1.Cust_FkId = s2.Cust_FkId AND s1.ProcessID=s2.ProcessID
AND s1.Service_Duration=s2.duration AND s1.ProcessID='4d2d6068678bc'
GROUP BY s1.Cust_FkId
It just goes away if there are more than 10K rows in that table. What it does is find rows for each customer who has min. of overall rank and in those max. of service duration for a given processid
Table Data
ID Cust_FkId Overall_Rank Service_Duration ProcessID
1 23 2 30 4d2d6068678bc
2 23 1 45 4d2d6068678bc
3 23 1 60 4d2d6068678bc
4 56 3 90 4d2d6068678bc
5 56 2 50 4d2d6068678bc
6 56 2 85 4d2d6068678bc
Result Data
Result ID values must be 3 and 6 only
Following select might be faster.
(Covering) Indexes on
Cust_FkID, Overall_Rank
Cust_FkID, Service_Duration
Cust_FkID, Overall_Rank, Service_Duration
Overall_Rank
Service_Duration
Remove the indexes that don't get used by looking at the execution plan.
SQL Statement
SELECT b.*
FROM binventory_ostemp b
INNER JOIN (
SELECT b.Cust_FkID
, ovr.Overall_Rank
, MAX(Service_Duration) AS Service_Duration
FROM binventory_ostemp b
INNER JOIN (
SELECT Cust_FkID
, MIN(Overall_Rank) AS Overall_Rank
FROM binventory_ostemp
GROUP BY
Cust_FkID
) ovr ON ovr.Cust_FkID = b.Cust_FKID
AND ovr.Overall_Rank = b.Overall_Rank
GROUP BY
b.Cust_FkID
, ovr.Overall_Rank
) ovrs ON ovrs.Cust_FkID = b.Cust_FkID
AND ovrs.Overall_Rank = b.Overall_Rank
AND ovrs.Service_Duration = b.Service_Duration