how to join two query result and grouped it by a column - php

i have query joined by union and it give me desirable result separately in rows i want to join those two rows and add its values but i don't know how to do it the query is
SELECT
`b`.`main_code`,
`b`.`account_title`,
SUM(CASE
WHEN
`a`.`type` = 'CP'
AND `a`.`interactive_person` = '6'
AND `a`.`date` BETWEEN '2018-02-12' AND '2018-07-31'
THEN
`a`.`balance`
ELSE 0
END) AS `credit`,
SUM(CASE
WHEN
`a`.`type` = 'CR'
AND `a`.`interactive_person` = '6'
AND `a`.`date` BETWEEN '2018-02-12' AND '2018-07-31'
THEN
`a`.`balance`
ELSE 0
END) AS `debit`,
SUM(CASE
WHEN
`a`.`type` = 'CR'
AND `a`.`interactive_person` = '6'
AND `a`.`date` BETWEEN '2018-02-12' AND '2018-07-31'
THEN
`a`.`balance`
WHEN `a`.`type` = 'CP' THEN - 1 * `a`.`balance`
ELSE 0
END) AS `balance`
FROM
`vouchers` AS `a`,
`data` AS `b`
WHERE
`a`.`interactive_person` = '6'
AND `a`.`post_status` = 'yes'
AND `a`.`interactive_person` = `b`.`main_code`
AND `a`.`date` BETWEEN '2018-02-12' AND '2018-07-31'
UNION SELECT
`b`.`main_code`,
`b`.`account_title`,
IFNULL(SUM(`a`.`debit`), 0) AS `debit`,
IFNULL(SUM(`a`.`credit`), 0) AS `credit`,
(`debit` - `credit`) AS `balance`
FROM
`journal_vouchers` AS `a`,
`data` AS `b`
WHERE
`a`.`account_id` = '44'
AND `a`.`date` BETWEEN '2018-02-12' AND '2018-07-31'
AND `post_status` = 'yes'
AND `cancel_status` = 'off'
AND `a`.`account_id` = `b`.`account_code`
GROUP BY `b`.`main_code`;
the query is a bit complex let me explain it little:
this query takes the result from 3 tables but main thing is that before union its take result from two table and after union two tables in which one table data interact with both. in short the result i get is
+-----------+---------------+--------+-------+---------+
| main_code | account_title | credit | debit | balance |
+-----------+---------------+--------+-------+---------+
| 6 | cash account | 5200 | 520 | -4680 |
+-----------+---------------+--------+-------+---------+
| 6 | cash account | 0 | 200 | -200 |
+-----------+---------------+--------+-------+---------+
i want the full query grouped by maincode but i dont know how do it
i want result to be
6 | cash account | 5200 | 320 | -4880

Related

how to retrieve one record when multiples exist

I need help only retrieving one record per job in this dataset. There are over 900 rows in the actual result set so this is a small sample but all rows look like the below. I am already using Distinct in the query but I get more than one record for the same job,suffix,and part. I need to be able to pull only one job,suffix,part but I need each value for workcenter,hours_estimated, and hours_actual associated with each job. I'm not sure if this should/could be done in the query or by using array methods in PHP. I almost found what I am looking for here but pervasive doesn't use CTE. Can anyone assist me in trying to get my data to look like below?
Sample Data
|Job |suffix| part |PL|qty| seq |workcenter|hours_estimated|hours_actual|
----------------------------------------------------------------------------
|A02043| 001 |913036|01| 2 |000400| 0710 | 0.7491 | 2.5700 |
|A02043| 001 |913036|01| 2 |000402| 0805 | 0.6420 | 0.0000 |
|A02043| 001 |913036|01| 2 |000500| 0901 | 16.1290 | 33.1600 |
|A02043| 001 |913036|01| 2 |000600| 1520 | 0.5000 | 0.0000 |
|A02900| 001 |913104|01| 1 |000500| 0710 | 0.5280 | 1.2000 |
|A02900| 001 |913104|01| 1 |000600| 0650 | 0.8540 | 0.0000 |
What I need my PDO object/array to look like
|Job |suffix| part |PL|qty| workcenter | hours_estimated | hours_actual |
-------------------------------------------------------------------------------------------------------------------
|A02043| 001 |913036|01| 2 |0710,0805,0901,1520| 0.7491, 0.6420,16.1290,0.500| 2.57,0,33.16,0 2.5700 |
|A02900| 001 |913104|01| 1 |0710,0650 |0.5280,0.8540 |1.200,0.0000 |
Query:
select distinct v_job_header.job,v_job_header.suffix,v_job_header.part,v_job_header.product_line,v_job_header.qty_order,v_job_operations_wc.seq,v_job_operations_wc.LMO,v_job_operations_wc.workcenter,v_job_operations_wc.hours_estimated,v_job_operations_wc.hours_actual,v_job_operations_wc.flag_closed,gab_source_cause_codes.source,gab_source_cause_codes.cause
from v_job_header
left join v_job_operations_wc on v_job_operations_wc.job = v_job_header.job and v_job_header.suffix = v_job_operations_wc.suffix
left join gab_source_cause_codes on gab_source_cause_codes.job = v_job_operations_wc.job and gab_source_cause_codes.suffix = v_job_operations_wc.suffix and gab_source_cause_codes.seq = v_job_operations_wc.seq
where v_job_header.product_line = '01' and v_job_header.date_closed < '2019-01-01' and v_job_operations_wc.LMO = 'L' and v_job_operations_wc.seq < '99000'
I had to change my query
select concat(concat(v_job_header.job,'-'),v_job_header.suffix) as Job,v_job_header.part,v_job_header.qty_order,
sum(case when v_job_operations_wc.workcenter = '0750' then v_job_operations_wc.hours_actual end) as WaterJet,
sum(case when v_job_operations_wc.workcenter IN ('0705','0710','0715') then v_job_operations_wc.hours_actual end) as Laser,
sum(case when v_job_operations_wc.workcenter IN ('0600','0610','1006','0650','1315') then v_job_operations_wc.hours_actual end) as Prep,
sum(case when v_job_operations_wc.workcenter IN ('1310','0755') then v_job_operations_wc.hours_actual end) as Machining,
sum(case when v_job_operations_wc.workcenter IN ('1515','1000','1002','1003','0901','1270') then v_job_operations_wc.hours_actual end) as Fab,
sum(case when v_job_operations_wc.workcenter = '1100' then v_job_operations_wc.hours_actual end) as Paint,
sum(case when v_job_operations_wc.workcenter = '1000' then v_job_operations_wc.hours_actual end) as Belts,
sum(case when v_job_operations_wc.workcenter = '1001' then v_job_operations_wc.hours_actual end) as Electrical,
sum(case when v_job_operations_wc.workcenter = '1520' then v_job_operations_wc.hours_actual end) as Crating_Skids,
sum(case when v_job_operations_wc.workcenter IN ('1004','1005','1350','1201') then v_job_operations_wc.hours_actual end) as Final_Assy,
sum(case when v_job_operations_wc.workcenter = '4330' then v_job_operations_wc.hours_actual end) as Shipping,
sum(v_job_operations_wc.hours_estimated) as total_hours_estimated,
gab_source_cause_codes.source,gab_source_cause_codes.cause
from v_job_header
left join v_job_operations_wc on v_job_operations_wc.job = v_job_header.job and v_job_header.suffix = v_job_operations_wc.suffix
left join gab_source_cause_codes on gab_source_cause_codes.job = v_job_operations_wc.job and gab_source_cause_codes.suffix = v_job_operations_wc.suffix and gab_source_cause_codes.seq = v_job_operations_wc.seq
where v_job_header.product_line = '01' and v_job_header.date_closed < '2019-01-01' and v_job_operations_wc.LMO = 'L' and v_job_operations_wc.seq < '99000'
group by Job,v_job_header.part,v_job_header.qty_order,gab_source_cause_codes.source,gab_source_cause_codes.cause

SELECT * FROM table WHERE `id` in ... and where sum() less

I'd like to make PHP $where as part of big query. I need something like:
SELECT *
FROM rf2aq_eb_events
WHERE id
IN ( SELECT event_id
, SUM(number_registrants) summ
FROM rf2aq_eb_registrants
WHERE summ < event_capacity
);
The rf2aq_eb_events table looks like:
ID | event_capacity
1 | 7
2 | 5
3 | 9
The rf2aq_eb_registrants table:
ID | events_id | number_registrants
1 | 1 | 6
2 | 2 | 2
3 | 3 | 4
4 | 1 | 1
5 | 2 | 0
6 | 3 | 5
I need select events from the 'rf2aq_eb_events' for events with quantity of registrant < then event_capacity. There is event id = 2 respond the condition.
i've tried $where[] = 'a.id IN ( SELECT event_id FROM #__eb_registrants GROUP BY event_id HAVING sum(number_registrants) < a.event_capacity)';
It's working like SQL, but do not in php in the whole Query.
Below i've put php result.
SELECT a.id, a.title, a.location_id,
a.event_capacity, a.event_date, a.individual_price,
a.thumb, a.early_bird_discount_date, a.early_bird_discount_amount,
c.name AS location_name
FROM #__eb_events AS a
LEFT JOIN #__eb_locations AS c
ON a.location_id = c.id
WHERE a.published =1
AND DATE(event_date) between date(CURDATE() + INTERVAL 7 DAY)
and date(CURDATE() + INTERVAL 26 DAY)
AND (
cut_off_date = "0000-00-00 00:00:00"
OR DATE(cut_off_date) between NOW() and date(CURDATE() + INTERVAL 26 DAY)
) AND a.id IN (
SELECT event_id
FROM #__eb_registrants
GROUP BY event_id
HAVING sum(number_registrants) < a.event_capacity
) AND a.id IN (
SELECT event_id FROM #__eb_event_categories
WHERE category_id IN (6,7)
) AND a.access IN (1,1)
ORDER BY a.event_date
LIMIT 4
You don't have to use subqueries.
SELECT `e`.* FROM `rf2aq_eb_events` as `e`
LEFT JOIN `rf2aq_eb_registrants` as `r`
ON `r`.`events_id`=`e`.`ID`
GROUP BY `r`.`events_id`
HAVING SUM(`r`.`number_registrants `) < `e`.`event_capacity`
You can't use the result of an aggregate (e.g. SUM) in a WHERE you can use it in a HAVING but you need to do a GROUP BY in this case as well:
SELECT * FROM `rf2aq_eb_events` e
WHERE `id` IN (
SELECT `event_id`
FROM `rf2aq_eb_registrants` r
GROUP BY `event_id`
HAVING sum(`number_registrants`) < `event_capacity`
)

Get the most popular result from theses tables

Here's my table definition:
Table ___Rooms:
|--------|-----------|--------|----------|
|ROO_Id |ROO_HotelId|ROO_Name|ROO_Number|
|--------|-----------|--------|----------|
| 1|AAA00 |Room 12 | 12|
| 2|AAA00 |Room 14 | 14|
| 3|AAA00 |Room 16 | 16|
| 4|ZZZ99 |Room 11 | 11|
| 5|ZZZ99 |Room 22 | 22|
| 6|ZZZ99 |Room 33 | 33|
|--------|-----------|--------|----------|
Table ___Bookings:
|--------|-----------|----------|
|BOO_Id |BOO_HotelId|BOO_RoomId|
|--------|-----------|----------|
| 1|AAA00 | 1|
| 2|AAA00 | 1|
| 3|AAA00 | 3|
| 4|ZZZ99 | 5|
| 5|ZZZ99 | 5|
| 6|ZZZ99 | 5|
|--------|-----------|----------|
Actually, I have:
Number of booking for AAA00 = 3
Number of rooms for AAA00 = 3
I want to list rooms for the property AAA00 only and rank them by the most popular in them of number of bookings.
So I use this query:
SELECT r.ROO_Number BOO_RoomId,
( ( ifnull(cnt_book,0)*100)/(SELECT count(*) FROM ___Bookings)) percentage,
ifnull(cnt_book,0) `count`
FROM ___Rooms r
LEFT JOIN (
SELECT BOO_RoomId, count(*) cnt_book
FROM ___Bookings
WHERE BOO_HotelId='AAA00'
GROUP BY BOO_RoomId
) cnt ON r.ROO_Id=cnt.BOO_RoomId
ORDER BY percentage DESC
The expecting result of this query was:
1 - Room 2 - 2 bookings - 66.66%
2 - Room 3 - 1 booking - 33.33%
3 - Room 2 - 0 booking - 00.00$
But it returns me all the rooms.
Could you please help me with that ?
Thanks.
Solution
Use CASE with SUM to add together all bookings per room. Then, JOIN to subquery to make the hotel total-bookings available to every row.
SELECT r.ROO_Name
, Sum(CASE WHEN BOO_id IS NULL THEN 0 ELSE 1 END) NumBookings
, Concat(
Format(
Sum(CASE WHEN BOO_id IS NULL THEN 0 ELSE 1 END)
/ TotalBookings
* 100
, 0)
, '%') AS PercentageTotal
FROM ( __Rooms r LEFT JOIN __Bookings b ON r.ROO_Id = b.BOO_RoomId
) INNER JOIN (SELECT BOO_HotelId
, Count(*) AS TotalBookings
FROM __Bookings
GROUP BY BOO_HotelId
) AS TotalHotelBookings
ON r.ROO_HotelId = TotalHotelBookings.BOO_HotelId
WHERE r.ROO_HotelId = 'AAA00'
GROUP BY r.ROO_Name
ORDER BY r.ROO_Name
;
Result Set
ROO_Name NumBookings PercentageTotal
-------- ----------- ---------------
Room 12 2 67%
Room 14 0 0%
Room 16 1 33%
Key point
Sum(CASE WHEN BOO_id IS NULL THEN 0 ELSE 1 END)
Pretty much should be something like this:
SELECT
foo.ROO_Id,
foo.ROO_Name,
foo.cnt,
(foo.cnt * 100.0) / (SELECT count(*) FROM ___Bookings WHERE BOO_HotelId = foo.ROO_HotelId) AS percentage
FROM (
SELECT
ROO_Id,
ROO_Name,
ROO_HotelId,
(SELECT count(*) FROM ___Bookings b WHERE b.BOO_RoomId = r.ROO_Id) AS cnt
FROM ___Rooms r
WHERE ROO_HotelId = 'AAA00'
) AS foo
ORDER BY cnt DESC

Use left join but the value no showing?

I have 2 table, game_jnship_equip_list is store equipment basic info, game_jnship_equip is store for what does player have.
Here is game_jnship_equip_list data:
+--------+----------------+----------+----------+
| ID | desc | name | type |
+--------+----------------+----------+----------+
| 1 | hello_weapon | weapon | 1 |
| 2 | hello_shirt | shirt | 2 |
| 3 | hell_weapon | Hweapon | 1 |
+-----------------------------------------------+
Here is game_jnship_equip data:
+------+----------+--------------+------------+------------+
| ID | userid | itemcode | atfigure | eposition |
+------+----------+--------------+------------+------------+
| 1 | 1 | 1 | 100 | 1 |
| 2 | 1 | 2 | 500 | 2 |
+----------------------------------------------------------+
And I query as below:
$quequip = DB::query("SELECT t1.*,t2.name AS weaponame, t2.edesc AS weapondesc, t3.atfigure AS atkbonus, t4.name AS shirtname, t4.edesc AS shirtdesc FROM ".DB::table('game_jnship_equip')." t1 LEFT JOIN ".DB::table('game_jnship_equip_list')." t2 ON (t1.itemid = t2.id AND t1.eposition = '1') LEFT JOIN ".DB::table('game_jnship_equip')." t3 ON (t2.type = t3.eposition) LEFT JOIN ".DB::table('game_jnship_equip_list')." t4 ON (t1.itemid = t4.id AND t1.eposition = '2') WHERE t1.uid = 'userid' AND t1.status = '1'");
$ruequip = DB::fetch($quequip);
But, I only can get value as below:
$ruequip['weaponame'] = weapon;
$ruequip['weapodesc'] = hello_weapon;
$ruequip['atkbonus'] = 100;
Then about the t4 all blank.
$ruequip['shirtname'] = ;
$ruequip['shirtdesc'] = ;
I want it to show value as below:
$ruequip['shirtname'] = shirt;
$ruequip['shirtdesc'] = hello_shirt;
So how to fix this? and my DB::query function, it cannot allow DB::query(SELECT * FROM xxxx (SELECT * FROM)), means 2 select inside 1 DB::query, system will reject by safety issue.
Thank you.
You can do this with conditional aggregation using CASE EXPRESSION instead of 3 left joins :
SELECT s.id,s.userid,s.itemcode,s.atfigure,s.eposition,
MAX(CASE WHEN s.eposition = 1 THEN s.desc END) as weap_desc,
MAX(CASE WHEN s.eposition = 1 THEN s.name END) as weap_name,
MAX(CASE WHEN s.eposition = 2 THEN s.desc END) as shirt_desc,
MAX(CASE WHEN s.eposition = 2 THEN s.name END) as shirt_name
FROM (SELECT t.id,t.userid,t.itemcode,t.atfigure,t.eposition,t2.desc,t2.name
FROM ".DB::table('game_jnship_equip')." t
LEFT OUTER JOIN ".DB::table('game_jnship_equip_list')." t2
ON(t1.itemid = t2.id AND t.eposition = t2.type) )s
GROUP BY s.user_id
Why not a union ?
SELECT
list.id as weaponid,
list.desc as weapondesc,
list.name as weaponame,
quip.atfigure as atkbonus
from ".DB::table('game_jnship_equip_list')." as list
left join ".DB::table('game_jnship_equip')." as quip
on (quip.itemcode = list.id)
Where list.type = 1
UNION
SELECT
list.id as shirtid,
list.desc as shirtdesc,
list.name as shirtname,
quip.atfigure as atkbonus
from elist as list
left join equip as quip
on (quip.itemcode = list.id)
Where list.type = 2
This will get you a specific type 2 and a specific type 1. I didn't added the other user and position conditions but you can do that yourself.

How to use SQL in the most efficient way with very big tables

I have a table purchase
something like (id, buyer_id, seller_id, amount) with 20-30 millions of records
Also i have a table with the same structure purchase_archive
and table users_balance (id, user_id, balance)
I should write a script that:
Moves records from purchase to purchase_archive table
For each moved row users_balance should be updated (user balance should be decreased for amount if he is a buyer, and increased for the same amount if he is a seller)
What is the best way to solve this task? (PHP + Mysql PDO)
My assumption is:
Set table engine to InnoDB
SELECT batch of 1000 rows from first table
Begin transaction (thats the reason for the InnoDB)
For each row
4.1 Store id in array ($temp)
4.2 Update balance with query like
SELECT `amount` FROM `purchase` WHERE `id` = :tid LIMIT 1 INTO #amount;
UPDATE `users_balance` SET `balance` = CASE
WHEN `user_id` = :seller_id THEN `balance` + #amount
WHEN `user_id` = :buyer_id THEN `balance` - #amount END
WHERE `user_id` IN (:buyer_id, :seller_id);
Move rows into archive with the query like that:
INSERT INTO `purchase_archive` SELECT * FROM `purchase` WHERE `id` IN (".$temp.");
DELETE QUICK FROM `transactions` WHERE `id` IN (".$temp.");
End transaction
And repeat 2-6 in cycle.
The longest operation is point 4.2, and i dont know how to perform it faster without variables
Is there any faster approaches?
P.S. Sorry for my terrible english.
You can try something like this:
update user_balance b
inner join (
select b.user_id,
sum(case when p.buyer_id = b.user_id then p.amount else 0 end) bought,
sum(case when p.seller_id = b.user_id then p.amount else 0 end) sold
from purchase p
inner join user_balance b
on p.buyer_id = b.user_id
or p.seller_id = b.user_id
group by b.user_id) q
on b.user_id = q.user_id
set b.amount = b.amount + q.sold - q.bought;
And it should do everything in a single query. You can limit the range further in the inner query if you wish. SQL Fiddle seems to be down so I can't provide a live demo, but there is this:
mysql> select * from user_balance;
+---------+--------+
| user_id | amount |
+---------+--------+
| 1 | 50 |
| 2 | 50 |
| 3 | 50 |
| 4 | 50 |
+---------+--------+
4 rows in set (0.00 sec)
mysql> select * from purchase;
+-------------+-----------+----------+--------+
| purchase_id | seller_id | buyer_id | amount |
+-------------+-----------+----------+--------+
| 1 | 1 | 2 | 10 |
| 2 | 3 | 4 | 20 |
| 3 | 4 | 2 | 5 |
| 4 | 1 | 4 | 7 |
| 5 | 3 | 1 | 9 |
+-------------+-----------+----------+--------+
5 rows in set (0.00 sec)
and after the query....
mysql> update user_balance b inner join (select b.user_id, sum(case when p.buyer_id = b.user_id then p.amount else 0 end) bought, sum(case when p.seller_id = b.user_id then p.amount else 0 end) sold from purchase p inner join user_balance b on p.buyer_id = b.user_id or p.seller_id = b.user_id group by b.user_id) q on b.user_id = q.user_id set b.amount = b.amount + q.sold - q.bought;
Query OK, 4 rows affected (0.07 sec)
Rows matched: 4 Changed: 4 Warnings: 0
mysql> select * from user_balance;
+---------+--------+
| user_id | amount |
+---------+--------+
| 1 | 58 |
| 2 | 35 |
| 3 | 79 |
| 4 | 28 |
+---------+--------+
4 rows in set (0.00 sec)

Categories