Codeigniter mysql query joining 2 values across 3 tables - mysql

Although I can get the joins to work, it doesn't return the lowest deal possible. The result should be lowest handset price possible with the lowest monthly cost. Currently I can get the lowest handset price, but not with the lowest monthly cost and similarly lowest monthly cost but not with the lowest priced handset.
I have 3 tables DEALS, TARIFFS and HANDSETS. When a customer comes to a manufacturer product listing page, it loads all products related to that manufacturer (in most instances around 40 products). What I would like is it to load the cheapest deal available for each product. Tariff table has over 4000 records. Handset table has over 3000 records. Deals table has over 1 million records.
There is a representation of the the 3 tables below with relevant columns:
Handset Table
==================================
id | manufacturer_id
-----------------------------------
100 1
Deals Table
==================================================
tariff_id | handset_id | handset_cost
--------------------------------------------------
15 100 44.99
20 100 114.99
Tariffs Table
==============================
id | monthly_cost
------------------------------
15 12.50
20 7.50
This is the query
$this->db->select('h.name, t.monthly_cost, d.handset_cost');
$this->db->from('aff_deals d');
$this->db->join('aff_tariffs t', 't.id = d.tariff_id', 'inner');
$this->db->join('aff_handsets h', 'h.id = d.handset_id', 'inner');
if (isset($manuid)) {
$this->db->where('h.manufacturer_id', $manuid);
}
$this->db->order_by('d.handset_cost ASC');
$this->db->order_by('t.monthly_cost ASC');
$this->db->group_by('h.name');
$query = $this->db->get();
$result = $query->result();
Unfortunately this returns handset cost of £114.99 and monthly cost of £7.50, when I need £44.99 and £12.50. The example above is a simple snap shot. I have tried MIN(d.handset_cost), sub-queries but cannot get the desired results. Somehow I need to be able to get the lowest price handsets, even if it's 0.00 (FREE), with its equivalent monthly cost. Any help would be most welcome.

According to your query you are misusing Mysql's GROUP BY extension without having any aggregate function this will lead your query to return indeterminate results for columns which are absent in group by taking your query as an example,columns t.monthly_cost, d.handset_cost values are indeterminate if you are specific to pick minimum row from deals table per handset then you can use below query
SELECT h.name,
t.monthly_cost,
d.handset_cost
FROM aff_deals d
INNER JOIN (SELECT handset_id,MIN(handset_cost) handset_cost
FROM aff_deals
GROUP BY handset_id) dd
ON d.handset_id = dd.handset_id AND d.handset_cost = dd.handset_cost
INNER JOIN aff_tariffs t ON t.id = d.tariff_id
INNER JOIN aff_handsets h ON h.id = d.handset_id
WHERE h.manufacturer_id =1
ORDER BY d.handset_cost ASC,t.monthly_cost ASC
See Demo
For active record query it will difficult to replicate above (subselect) query bu you can directly run this query through $this->db->query('your query')

Related

Laravel MySql get attendance for players for match

Currently I am designing an app for Football Club which organises friendly match every weekend.
The system opens entry for players on Wednesday morning till Thursday 6PM. So players give their availability if they are available for match on that weekend or not and club organises accordingly.
I have 2 tables matches which saves match information like following.
id | date | entry_open_time | entry_close_time | active | deleted | created | modified
Another table is availabilities table which saves players availability
id | match_id | player_id | is_available | deleted | created | modified
How can I calculate who is top of the leaderboard by attendance percentage for the year?
Currently I do the following.
Run 2 queries, one to get total number of matches organised in current year and second get total attendance count per user
Then I calculate percentage of attendance for each user looping through collection.
Then sort the collection.
Is there a better way to do this? May be get the results directly using mysql query?
Total Match count query
Match::where('active', true)->count();
Attendance by user
User::active()->withCount([
'availabilities' => function ($query) {
$query->available();
},
])->orderBy('availabilities_count')->get()
Then I calculate the percentage for each user in collection
$availabilities->map(function ($item) use ($matchCount) {
if ($item['availabilities_count'] && $matchCount) {
$percentage = ($item['availabilities_count'] * 100) / $matchCount;
$item['attendance'] = round($percentage,2);
} else {
$item['attendance'] = 0;
}
return $item;
});
After that I sort the collection by attendance
$availability->sortBy('attendance');
Here is the sample SqlFiddle
How can I get same result with mysql query?
Thank you
I believe in Mysql you can achieve your expected results as
select `u`.*,
ac.availabilities_count,
mc.match_count,
round((ac.availabilities_count/ mc.match_count) * 100,2) attendance
from `users` u
join (
select user_id, count(*) availabilities_count
from `availabilities`
where `is_available` = 1
group by user_id
) ac on `u`.`id` = `ac`.`user_id`
join (
select count(*) match_count
from matches
-- where active = 1
) mc
order by `first_name`, `last_name`
DEMO

Total amount of sales done for each product using SQL

Here is the structure of 1st Table called Product.
PRODID PDESC PRICE CATEGORY DISCOUNT
101 BALL 10 SPORTS 5
102 SHIRT 20 APPAREL 10
Here is the structure of 2nd table called SaleDetail.
SALEID PRODID QUANTITY
1001 101 5
1001 101 2
1002 102 10
1002 102 5
I am trying to get total sales amount for each product by joining 2 tables. Here is the SQL i tried but its not giving correct result.
select a.prodid,
(sum((price - discount))),
sum(quantity),
(sum((price - discount))) * sum(quantity)
from product a
join saledetail b on a.prodid = b.prodid
group by a.prodid
2nd column of the query is giving incorrect final price. Please help me correct this SQL.
Please find an indicative answer to your question in the fiddle.
A problem stems from the aggregation of the difference of price. In case that the same product has two different prices, then these prices would be aggregated to one.
Moreover, you multiple the sums of the prices and quantities, while you need to perform the calculation on every sample. Look at the answer by #DanteTheSmith.
You might consider to use the SaleDetail table on the left side of your query.
SELECT SD.PRODID,
P.Price-P.Discount AS Final_Price,
SUM(SD.QUANTITY) AS Amount_Sold,
SUM((P.Price-P.Discount)*SD.QUANTITY) AS Sales_Amount
FROM SaleDetail AS SD
JOIN Product AS P
ON SD.PRODID = P.PRODID
GROUP BY SD.PRODID, P.Price-P.Discount
It would help if you built the example in SQL fiddle or gave the creates for the tables, but if I have to guess your problem is:
(sum((price - discount))) * sum(quantity)
needs to be:
sum((price - discount) * quantity)
(price - discount) * quantity is the function you wanna apply PER ROW of the joined table then you wanna add all those up with SUM() when grouping by prodid.
Furthermore, you can notice that (price - discount) needs to be done ONLY ONCE PER ROW so a quicker version would be to do:
(price-discount) * sum(quantity)
That would give you the total money earned for that product across all the sales you made, and I am guessing this is what you want?
I just notice you have a problem with 2nd column, dunno if that has been in question all along:
(sum((price - discount)))
Why are you summing? Do you want the money earned per product per unit of the product? Well guess what, your price is the same all the time, same as your discount so you can simply go with:
(price-discount) as PPP
NOTE: This assumes the discount is numerical (not percentage) and is applicable to all your sales, also the price is forever the same all which is not real life like.

multiply two columns from two different tables and group them together

I have two tables in MySQL which are sale and material details.
I want to calculate the profit I made by selling items which is
profit = (total) -(qty* landedcost)
Here is the structure of the two tables:
This is the query
SELECT sale.name ,sale.total-(sale.qty * materialdetails.landingcost) AS
result
FROM sale JOIN materialdetails
on sale.id = materialdetails.id
GROUP BY sale.name,result;
the result i get :
query result
I want something like this
name result
A4 5000
Computer 40000
Flash memory 1000
Headphone 22000
Mobile 35000
Any idea please?
You should sum the result and group by sale.name only, something like this:
SELECT sale.name ,sum(sale.total-(sale.qty * materialdetails.landingcost)) AS
result
FROM sale JOIN materialdetails
on sale.id = materialdetails.id
GROUP BY sale.name;
Explanation: if you group by two fields GROUP BY sale.name,result you will get one line for all records that have the same sale.name and result, so for instance
name result
Computer 10000
Computer 25000
are two different lines and they are not grouped together as one.

INSERT interpolated rows into existing table

I have a MySQL table similar to this simplified example:
orders table
--------------------------------
orderid stockid rem_qty reported
--------------------------------
1000000 100 500 00:01:00
1000000 100 200 01:10:00
1000000 100 200 03:20:00
1000000 100 100 04:30:00
1000000 100 50 11:30:00
:
1000010 100 100 00:01:00
1000010 100 100 01:10:00
1000010 100 20 03:20:00
:
1000020 200 1000 03:20:00
1000020 200 995 08:20:00
1000020 200 995 11:50:00
--------------------------------
The table comes from a 3rd party, weighs in at some 80-100M rows daily, and the format is fixed. It would be good, except it lacks rows showing when rem_qty reaches zero. The good news is, I can estimate them, at least a good upper/lower bound:
The 3rd party scans each distinct stockid at essentially random times throughout the day, and returns one row for each open orderid at that time. For example, stockid = 100 was scanned at (00:01, 01:10, 03:20, 04:30, 11:30). At each time, there will be a row for every current orderid with that stockid. Hence, one can see that orderid = 1000000 was still open at 11:30 (the last scan in our data), but sometime between 03:20 and 04:30, orderid = 1000010 sold out. (The times for stockid = 200 have no bearing on stockid = 100).
So, what I would like to do is INSERT the interpolated rows with rem_qty = 0 for each sold-out order. In this case, we can (only) say that orderid = 1000010 went to 0 at AVG('03:20:00','04:30:00'), so I would like to INSERT the following row:
orders table INSERT
--------------------------------
orderid stockid rem_qty reported
--------------------------------
1000010 100 0 03:55:00
--------------------------------
Trouble is, my SQL is rusty and I've not been able to figure out this complex query. Among other failed attempts, I've tried various JOINs, made a TEMPORARY TABLE stock_report(stockid,last_report), and I can do something like this:
SELECT orders.stockid,
orderid,
MAX(reported),
TIMEDIFF(last_report,MAX(reported)) as timediff
FROM orders
INNER JOIN stock_report
ON orders.stockid = stock_report.stockid
GROUP BY orderid
HAVING timediff > 0
ORDER BY orderid
This would show every sold-out order, along with the HH:MM:SS difference between the last time the orderid was reported, and the last time its stockid was reported. It's maybe a good start, but instead of last_report, I need to be able to calculate a next_report column (specific to this orderid, which would basically be:
SELECT MIN(reported) AS next_report
FROM orders
WHERE reported > #order_max_reported
ORDER BY reported
LIMIT 1
But that's just a vain attempt to illustrate part of what I'm after. Again, what I really need is a way to INSERT new rows into the orders() table at the AVG() time the order's rem_qty went to 0, as in the orders table INSERT example table, above. Or, maybe the 64,000 GFLOP question: would I be better off moving this logic to my main (application) language? I'm working with 100 million rows/day, so efficiency is a concern.
Apologies for the lengthy description. This really is the best I could do to edit for conciseness! Can anyone offer any helpful suggestions?
Possible to do. Have a sub query that gets the max reported time for each order id / stock id and join that against the orders table where the stock id is the same and the latest time is less that the order time. This gets you all the report times for that stock id that are greater than the latest time for that stock id and order id.
Use MIN to get the lowest reported time. Convert the 2 times to seconds, add them together and divide by 2, then convert back from seconds to a time.
Something like this:-
SELECT orderid, stockid, 0, SEC_TO_TIME((TIME_TO_SEC(next_poss_order_report) + TIME_TO_SEC(last_order_report)) / 2)
FROM
(
SELECT a.orderid, a.stockid, last_order_report, MIN(b.reported) next_poss_order_report
FROM
(
SELECT orderid, stockid, MAX(reported) last_order_report
FROM orders_table
GROUP BY orderid, stockid
) a
INNER JOIN orders_table b
ON a.stockid = b.stockid
AND a.last_order_report < b.reported
GROUP BY a.orderid, a.stockid, a.last_order_report
) sub0;
SQL fiddle here:-
http://www.sqlfiddle.com/#!2/cf129/17
Possible to simplify this a bit to:-
SELECT a.orderid, a.stockid, 0, SEC_TO_TIME((TIME_TO_SEC(MIN(b.reported)) + TIME_TO_SEC(last_order_report)) / 2)
FROM
(
SELECT orderid, stockid, MAX(reported) last_order_report
FROM orders_table
GROUP BY orderid, stockid
) a
INNER JOIN orders_table b
ON a.stockid = b.stockid
AND a.last_order_report < b.reported
GROUP BY a.orderid, a.stockid, a.last_order_report;
These queries might take a while, but are probably more efficient than running many queries from scripted code.

2 criterias in the same query

i need some help...
In the same query can i have 2 different criteria?
Ex: I need to select, product type, product color and product price between 1000 to 2000.
In this research 10 products for example will be shown, and i would like to continue showing other products only with the price criterion.
The first 10 products are on the correct criteria and other products will be displayed with the price criterion.
Is this possible?
Thank you!
It's possible. You have to separate your criterias with AND :
SELECT * FROM ... WHERE product_type = 'something' AND product_color = 'something' AND product_rate > 1000 AND product_rate < 2000