How do I sum [add] multiple columns from joined tables - mysql

I need to write a query that joins several tables and totals several columns in different tables & can't seem to figure out how to do it:
here are the tables:
The result I am trying to get is a result with the contract budgets for a particular budget name added: i.e. [this does not work but gives an idea]
select c.contract_budget_f1, c.contract_budget_f2, cb.budget_type_id, c.id, sum(cb.budget_f1) as bf1, sum(cb.budget_f2) as bf2
from `flow_contract` c
left join `flow_contract_budget` cb on cb.contract_id = c.id
where c.program_id = '69'
group by cb.budget_type_id
with the whole result set looking like:
[budget_type_id]
[contract_budget_f1]
[contract_budget_f2]
[bf1]
[bf2]
where it will return 3 rows with the budgets for each budget type added
How can I do this, is it even possible?
Here are links to the tables - sorry, didn't realize you couldn't click on them...
http://media.bigblockstudios.ca/stack/program-name.gif
http://media.bigblockstudios.ca/stack/contract-budget.gif
http://media.bigblockstudios.ca/stack/contracts.gif
UPDATE
I got it working like this:
select c.id, c.program_id, c.contract_budget_f1, c.contract_budget_f2, cb.budget_f1, cb.budget_f2, cb.budget_type_id, c.id,
sum(cb.budget_f1) as bf1, sum(cb.budget_f2) as bf2
from `flow_contract` c
left join `flow_contract_budget` cb on cb.contract_id = c.id
where c.program_id = '".$formfields['program_id']."'
group by cb.budget_type_id
order by cb.budget_type_id

I think what you want is:
group by cb.budget_type_id, cb.contract_id
Or the other way around. Could you make an sql fiddle?

Short Answer: subquery
Try this:
Start with a subquery.
Write a query that produces the column values that you want to sum. This is now the subquery.
Write an outter query that sums the desired columns in the inner query.
After you get it working, review it to see if you can optimize out the subquery and just have one query.

Related

SQL Temporary Table or Select

I've got a problem with MySQL select statement.
I have a table with different Department and statuses, there are 4 statuses for every department, but for each month there are not always every single status but I would like to show it in the analytics graph that there is '0'.
I have a problem with select statement that it shows only existing statuses ( of course :D ).
Is it possible to create temporary table with all of the Departments , Statuses and amount of statuses as 0, then update it by values from other select?
Select statement and screen how it looks in perfect situation, and how it looks in bad situation :
SELECT utd.Departament,uts.statusDef as statusoforder,Count(uts.statusDef) as Ilosc_Statusow
FROM ur_tasks_details utd
INNER JOIN ur_tasks_status uts on utd.StatusOfOrder = uts.statusNR
WHERE month = 'Sierpien'
GROUP BY uts.statusDef,utd.Departament
Perfect scenario, now bad scenario :
I've tried with "union" statements but i don't know if there is a possibility to take only "the highest value" for every department.
example :
I've also heard about
With CTE tables, but I don't really get how to use it. Would love to get some tips on it!
Thanks for your help.
Use a cross join to generate the rows you want. Then use a left join and aggregation to bring in the data:
select d.Departament, uts.statusDef as statusoforder,
Count(uts.statusDef) as Ilosc_Statusow
from (select distinct utd.Departament
from ur_tasks_details utd
) d cross join
ur_tasks_status uts left join
ur_tasks_details utd
on utd.Departament = d.Departament and
utd.StatusOfOrder = uts.statusNR and
utd.month = 'Sierpien'
group by uts.statusDef, d.Departament;
The first subquery should be your source of all the departments.
I also suspect that month is in the details table, so that should be part of the on clause.

SQL LEFT JOIN with possible join condition duplicate match

Here is my query so far:
SELECT
b.cs_bidding_id, b.cs_bidding_user_id,
floor(AVG(u.cs_rating)) AS cs_user_rating
FROM cs_biddings b LEFT JOIN
cs_user_ratings u ON u.cs_user_rated_id = b.cs_bidding_user_id
I would like to get the avg rating of the user's per bidding post.
However this does not work for multiple biddings because whenever the join condition is satisfied, it wont let me fetch other avg rating for other biddings that shares the same bidding_user_id
desired result:
Unsummarized Query:
SELECT
b.cs_bidding_id,
b.cs_bidding_title,
b.cs_bidding_details,
b.cs_bidding_user_id,
b.cs_bidding_permalink,
b.cs_bidding_added,
b.cs_bidding_picture,
b.cs_bidding_status,
b.cs_bidding_location,
floor(AVG(u.cs_rating)) AS cs_owner_rating
FROM cs_biddings b LEFT JOIN
cs_user_ratings u ON u.cs_user_rated_id = b.cs_bidding_user_id
Your query is malformed. It is an aggregation query (because of the AVG()) but the SELECT columns are inconsistent with the aggregation columns (well, there are none of those).
Fixing the group by might fix your problem:
SELECT b.cs_bidding_id, b.cs_bidding_user_id,
floor(AVG(u.cs_rating)) AS cs_user_rating
FROM cs_biddings b LEFT JOIN
cs_user_ratings u
ON u.cs_user_rated_id = b.cs_bidding_user_id
GROUP BY b.cs_bidding_id, b.cs_bidding_user_id;
I'm not sure if you want both columns in the GROUP BY (and the result set). However, without sample data and desired results, it is unclear what you actually intend.

something wrong with the result of mysql query with joins and select

Good day,
I am trying to join 3 tables for my inventory report but I am getting weird results out of it.
my query
SELECT i_inventory.xid,
count(x_transaction_details.xitem) AS occurrence,
i_inventory.xitem AS itemName,
SUM(i_items_group.or_qty) AS `openingQty`,
avg(x_transaction_details.cost) AS avg_cost,
SUM(x_transaction_details.qty) AS totalNumberSold,
SUM(i_items_group.or_qty) - SUM(x_transaction_details.qty) AS totalRemQty
FROM x_transaction_details
LEFT JOIN i_inventory ON x_transaction_details.xitem = i_inventory.xid
LEFT JOIN i_items_group ON i_inventory.xid = i_items_group.xitem
WHERE (x_transaction_details.date_at BETWEEN '2015-01-18 03:14:54' AND '2015-10-18 03:14:54')
AND i_inventory.xid = 3840
GROUP BY x_transaction_details.xitem
ORDER BY occurrence DESC
This query gives me this result:
See the openingQty column, I then tried to do a simple query to verify the result,
here's my query for checking the openingQty with joining only 2 tables i_items_group table (batches are stored) and i_inventory table (item Information are stored).
SELECT i_inventory.xid,
i_inventory.xitem,
SUM(i_items_group.or_qty) AS openingQty,
i_items_group.cost
FROM i_inventory
INNER JOIN i_items_group ON i_inventory.xid = i_items_group.xitem
WHERE i_inventory.xid = 3840
AND (i_items_group.date_at BETWEEN '2015-01-18 03:14:54' AND '2015-10-18 03:14:54')
my result was:
which is the correct data.
I also made a query on my x_transaction_details table also to verify if its correct or not.
heres my query:
select xitem, qty as qtySold from x_transaction_details where xitem = 3840
AND (date_at BETWEEN '2015-01-18 03:14:54' AND '2015-10-18 03:14:54')
result:
Which would total to: 15-quatitySold.
I'm just confused on how did I get 3269 as a result of my query where as the true openingQty should be only 467.
I guess the problem was in my query with joins, its messing up with number of transactions then it sums it up (I really dont know though).
Can you please help me identify it, and help me come up with the correct query.
This is a common problem with multiple SUM statements in a single query. Keep in mind how SQL does aggregation: first it generates a set of data that is not aggregated, then it aggregates it. Try your query without the GROUP BY or aggregate functions, and you'll be surprised what you turn up. There aren't enough of the right details in your post for me to determine where the breakdown is, but I can guess.
It looks like you have an xitem corresponding to some kind of product, then you have joined that to both transactions and items groups. Suppose a particular xitem matches with 3 transactions and 5 item groups. You'll get 15 records from that join. And when you sum it, any SUM calculations based on fields from the transaction table will be 5x higher than you expect, and any SUM calculations from the item groups table will be 3x higher than you expect. The key symptom here is the aggregate result being a multiple of the correct value, but seemingly different multiples for different rows.
There are multiple ways to address this kind of error. Some developers like to calculate one of hte aggregates in a subquery, then do the other aggregate in the main query and group by the already correct result from the subquery. Others like to write in-line queries to do the aggregate right in the expression:
SELECT xitem, (SELECT SUM(i_items_group.or_qty) FROM i_items_group WHERE i_inventory.xid = i_items_group.xitem) AS `openingQty`
, -- select more fields
Find what approach works best for you. But if you want to see the evidence for yourself, run this query with the aggregates gone and you'll see why those SUMs are doing what they are doing:
SELECT i_inventory.xid,
x_transaction_details.xitem AS occurrence,
i_inventory.xitem AS itemName,
i_items_group.or_qty,
x_transaction_details.cost,
x_transaction_details.qty,
i_items_group.or_qty - x_transaction_details.qty AS RemainingQty
FROM x_transaction_details
LEFT JOIN i_inventory ON x_transaction_details.xitem = i_inventory.xid
LEFT JOIN i_items_group ON i_inventory.xid = i_items_group.xitem
WHERE (x_transaction_details.date_at BETWEEN '2015-01-18 03:14:54' AND '2015-10-18 03:14:54')
AND i_inventory.xid = 3840
ORDER BY occurrence DESC

Count() on joins for pagination, getting additional rows

I have this problem that when i run this one.
$results = mysqli_query($connecDB,"SELECT COUNT(*) FROM customers
RIGHT JOIN orders on customers.serial=orders.serial
RIGHT JOIN order_detail on orders.serial=order_detail.orderid
LEFT JOIN inventory on order_detail.productid=inventory.prod_id");
$get_total_rows = mysqli_fetch_array($results); //total records
it shows additional pages even if it's blank. i've tried on other tables that doesnt require me to join and it works on them. But with this one, it does not work perfectly.
How do i fix my count function?
here is my original query before tweaking the pagination codes.
SELECT DISTINCT customers.order_status,customers.serial,customers.name,customers.address,customers.phone,customers.email,customers.payment,customers.carrier,customers.tracking_no, orders.date, order_detail.productid, order_detail.quantity, order_detail.price, inventory.prod_name
FROM customers
RIGHT JOIN orders on customers.serial=orders.serial
RIGHT JOIN order_detail on orders.serial=order_detail.orderid
LEFT JOIN inventory on order_detail.productid=inventory.prod_id
GROUP BY orders.date, order_detail.orderid
ORDER BY order_detail.orderid DESC";
To much to put in a comment:
There are three ways to get the total records for use with pagination.
Execute the query without the limit and get the number of records in the resultset. It maybe obvious that this isn't the way to go.
Execute the query with the limit and use SQL_CALC_FOUND_ROWS.
Replace the colums in the SELECT clause with COUNT(1) and fetch the (single) row.
In many situations the third is the best choice, however sometimes the second can out perform the third.
Finally, see this predicates
A product which isn't in inventory can't be ordered
An order can't be submitted by non-customers
I think inner joins are sufficient

How to refine this sql query with a LEFT JOIN?

I have a problem with the following SQL Query. As you can see, I join some tables (candidate_basic,candidate_lang,province_of_candidate,province,degree_of_candidate), and then I join the result with another table "professional_experience_basic" but, the query return several instances (example: it returns two instances for id_candidate_basic=55, or it returns three instances of id_candidate_basic=59).
My problem is that I only want that the query return 1 instance per id_candidate_basic. That is, the instance with attribute "main_job" with value 1, and if this is not possible, just a null.
I tried to put AND professional_experience_basic.main_job=1 inside the WHERE clause, but, it remove the instances with main_job=null.
I feel that I'm close to the final result but I have spent lots of minutes in this last effort. Does anyone know the solution?
Thanks in advance.
The Query:
SELECT DISTINCT
candidate_basic.id_candidate_basic,
candidate_lang.town,
province.name,
degree_of_candidate.id_degree_of_candidate,professional_experience_basic.main_job
FROM (((((candidate_basic
INNER JOIN candidate_lang
ON candidate_lang.id_candidate_basic=candidate_basic.id_candidate_basic)
INNER JOIN province_of_candidate
ON province_of_candidate.id_candidate_basic=candidate_basic.id_candidate_basic)
INNER JOIN province
ON province.id_province=province_of_candidate.id_province)
INNER JOIN degree_of_candidate
ON degree_of_candidate.id_candidate_basic=candidate_basic.id_candidate_basic)
LEFT JOIN professional_experience_basic
ON professional_experience_basic.candidate_id=candidate_basic.id_candidate_basic)
WHERE candidate_basic.candidate_state=2
AND degree_of_candidate.is_main_degree
AND candidate_lang.id_website_lang=1
The current result:
The result I want:
Change professional_experience_basic.main_job to max(professional_experience_basic.main_job) in the column list, then:
GROUP BY candidate_basic.id_candidate_basic, candidate_lang.town,
province.name, degree_of_candidate.id_degree_of_candidate
First Group your Result
... AND candidate_lang.id_website_lang=1
GROUP BY
candidate_basic.id_candidate_basic,
candidate_lang.town,
province.name,
degree_of_candidate.id_degree_of_candidate
And i think you want the max of main_job, but i dont know.
change select to:
SELECT DISTINCT
candidate_basic.id_candidate_basic,
candidate_lang.town,
province.name,
degree_of_candidate.id_degree_of_candidate,MAX(professional_experience_basic.main_job)
It seems like you want the max here, but i dont know