Struggling adding WHERE clause to INNER JOIN - php

I have a query which is supposed to select the lowest price_per_pax_after_tax from every backend_hotels_id date_start and package_supplier and this appears to be working until I add a WHERE clause.
Here's the query:
SELECT e.price_per_pax_after_tax, e.hotel_score, e.package_id, e.package_type
FROM packages_sorted_YQU e
INNER JOIN (
SELECT db_id, MIN( price_per_pax_after_tax ) AS lowest_price, package_id, hotel_score
FROM `packages_sorted_YQU`
WHERE `package_type` IN ('9', '10', '18')
AND `package_duration` IN ('6', '8', '12')
GROUP BY
`date_start` , `package_supplier` , `backend_hotels_id`
) AS j
ON j.db_id = e.db_id
AND j.lowest_price= e.price_per_pax_after_tax
AND j.hotel_score = e.hotel_score
AND j.package_id = e.package_id;
The table is huge but all of the fields listed are INT except for date_start which is DATE
The where clause causing the problem is:
WHERE `package_type` IN ('9', '10', '18')
AND `package_duration` IN ('6', '8', '12')
Without the where clause I get over 400 results and with the where clause I get zero results :( Any help will be very much appreciated.

If your columns package_type and package_duration are of type int you don't have to wrap the values inside ' like strings.
SELECT e.price_per_pax_after_tax, e.hotel_score, e.package_id, e.package_type
FROM packages_sorted_YQU e
INNER JOIN (
SELECT db_id, MIN( price_per_pax_after_tax ) AS lowest_price, package_id, hotel_score
FROM `packages_sorted_YQU`
WHERE `package_type` IN (9, 10, 18)
AND `package_duration` IN (6, 8, 12)
GROUP BY
`date_start` , `package_supplier` , `backend_hotels_id`
) AS j
ON j.db_id = e.db_id
AND j.lowest_price= e.price_per_pax_after_tax
AND j.hotel_score = e.hotel_score
AND j.package_id = e.package_id;

The subquery:
SELECT db_id
, MIN( price_per_pax_after_tax ) AS lowest_price
, package_id
, hotel_score
FROM `packages_sorted_YQU`
WHERE `package_type` IN ('9', '10', '18')
AND `package_duration` IN ('6', '8', '12')
GROUP BY
`date_start`
, `package_supplier`
, `backend_hotels_id`
will yield indeterminate results, with or without the WHERE clause. Because you are grouping by date_start, package_supplier, backend_hotels_id and have in the SELECT list columns without any aggregate functions on them: db_id, package_id, hotel_score.
This query should work consistently if the (date_start, package_supplier, backend_hotels_id) is the Primary Key or Unique.
Which is the PRIMARY KEY of the table and are there any other UNIQUE keys?

Hi all and thank you for your valuable input. I've solved the problem without a sub-query and it works a bit faster too.
SELECT MIN
(
concat
(
LPAD(`price_per_pax_after_tax` , 5, '0'),
LPAD(`package_id` , 12, '0'),
LPAD(`hotel_score` , 7, '0')
)
) AS cat
FROM `packages_sorted_YQU`
WHERE `package_type` IN
(
9, 10, 18
)
AND `package_duration` IN
(
6, 7, 8
)
GROUP BY `date_start` , `package_supplier` , `backend_hotels_id`
Then in PHP I break apart the concatenation with:
while($r=mysql_fetch_array($q,MYSQL_ASSOC))
{
$a[lrp][] = intval(substr($r[cat], 0, 5));
$a[package_id][] = intval(substr($r[cat], 5, 12));
$a[hotel_score][] = substr($r[cat], 17, 7);
}
I was lucky that the only FLOAT value was the hotel_score so I put that last - the other two were of type INT

Related

Get all rows inside a foreach loop and echo an array result?

I'm trying to get all rows inside a foreach loop but it's not working as it should.
<?php
foreach ($locations_loop as $row):
$lr_id = $row["id"];
$stmtlr = $pdo->prepare("SELECT * FROM locations_rating WHERE l_id = {$lr_id}");
$stmtlr->execute();
$stlr_loop = $stmtlr->fetchAll(PDO::FETCH_ASSOC);
if (empty($stlr_loop)) {
$loc_rate[] = "0";
} else {
foreach($stlr_loop as $rowlr):
$loc_rate[] = $rowlr["stars"];
endforeach;
}
$rating_array = array_values($loc_rate);
$rating_avg = array_sum($rating_array) / count($rating_array);
?>
<?=round($rating_avg, 1);?>
<?php endforeach; ?>
$rating_avg outputs something else every time the script runs. It works fine outside a foreach loop tho. I tried to join the two table but no luck since it only outputs only one row.
I might be thinking too far out of the box, but this is just one technique that occurred to me which will ensure that all location ids will receive an average value in the result set.
Assuming $locations_loop (a poor name for a variable containing array type data, tbh) has the following data:
$locations_loop = [
['id' => 1],
['id' => 2],
['id' => 3],
['id' => 4],
];
And you have a database table with the following schema: (db-fiddle demo)
CREATE TABLE `locations_rating` (
`id` int(11) NOT NULL,
`l_id` int(11) NOT NULL,
`stars` int(11) NOT NULL DEFAULT 0
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `locations_rating` (`id`, `l_id`, `stars`) VALUES
(1, 3, 4),
(2, 2, 2),
(3, 1, 0),
(4, 2, 5),
(5, 3, 2),
(6, 1, 10);
Then you can get all of your data in one trip to the database by creating a "derived table" from your column of id values, then joining the database data to them. Something like this:
SELECT def.l_id,
ROUND(AVG(COALESCE(stars, 0)), 1) avg
FROM (
(SELECT 1 AS l_id)
UNION (SELECT 2)
UNION (SELECT 3)
UNION (SELECT 4)
) AS def
LEFT JOIN locations_rating AS loc ON def.l_id = loc.l_id
GROUP BY def.l_id
To do this with a prepared statement and bound parameters:
$locationIds = array_column($locations_loop, 'id');
$countIds = count($locationIds);
$fabricatedRows = implode(' UNION ', array_fill(0, $countIds, '(SELECT ? AS l_id)'));
$sql = "SELECT derived.l_id,
ROUND(AVG(COALESCE(stars, 0)), 1) avg
($fabricatedRows) AS derived
LEFT JOIN locations_rating as loc ON derived.l_id = loc.l_id
GROUP BY def.l_id";
$stmt = $pdo->prepare($sql);
$stmt->execute($locationIds);
var_export($stmt->fetchAll(PDO::FETCH_ASSOC));
Should output: (I tested this technique to be successful in my local environment)
[
['l_id' => 1, 'avg' => 5.0],
['l_id' => 2, 'avg' => 3.5],
['l_id' => 3, 'avg' => 3.0],
['l_id' => 4, 'avg' => 0.0],
]

How to manipulate and collate data in an associate array

I have the following array of data
2889 1 1062
2889 8 John Smith
2889 6 0.29
2891 1 1117
2891 8 Jamie Dean
2891 6 2
2892 1 1062
2892 8 John Smith
2892 6 4
The First column is a list of entry IDs relating to form entries from a website, the second column is a list of meta_keys relating to fields in the form, and the final column is the data from those fields.
What I need to be able to do is collate the data in the array so that for each person I have:
ID Number(Meta_key 1)
Name(Meta_key 8)
Sum(Hours Owed(Meta_key 6))
I am lost on how to even start this task, any help would be very much appreciated.
This data has all been pulled from a database with the following query:
select
entry_id,
meta_key,
meta_value
from
staff_gf_entry_meta
where
form_id = 48
and
entry_id in (
select
entry_id
from
staff_gf_entry_meta
where
meta_key = 7
and
form_id = 48
and
meta_value <= '2018-12-18'
and
meta_value >= '2018-12-12'
)
and (
meta_key = 1
or
meta_key = 8
or
meta_key = 6)
If needed the query can be altered.
You might want a GROUP BY statement with the SUM aggregate function. To get all the key/value pairs in a row, you need to JOIN multiple queries.
SELECT
`mk1`.`meta_value` `Number`,
`mk8`.`meta_value` `Name`,
SUM(`mk6`.`meta_value`) `Hours Owed`
FROM
`staff_gf_entry_meta` `mk1`
INNER JOIN
`staff_gf_entry_meta` `mk6`
USING
(`entry_id`, `form_id`)
INNER JOIN
`staff_gf_entry_meta` `mk7`
USING
(`entry_id`, `form_id`)
INNER JOIN
`staff_gf_entry_meta` `mk8`
USING
(`entry_id`, `form_id`)
WHERE
`mk1`.`meta_key` = 1
AND
`mk6`.`meta_key` = 6
AND
`mk7`.`meta_key` = 7
AND
`mk8`.`meta_key` = 8
AND
`mk1`.`form_id` = 48
AND
`mk7`.`meta_value` BETWEEN '2018-12-12' AND '2018-12-18'
GROUP BY `mk1`.`meta_value`,`mk1`.`form_id`
;
I've assumed the following table structure and data:
CREATE TABLE `staff_gf_entry_meta`
(
`form_id` int(11) NOT NULL,
`entry_id` int(11) NOT NULL,
`meta_key` int(11) NOT NULL,
`meta_value` varchar(45) DEFAULT NULL,
PRIMARY KEY (`entry_id`,`form_id`,`meta_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
;
INSERT INTO `staff_gf_entry_meta` (form_id, entry_id, meta_key, meta_value)
VALUES
(48, 2889, 1, 1062),
(48, 2889, 8, 'John Smith'),
(48, 2889, 6, 0.29),
(48, 2891, 1, 1117),
(48, 2891, 8, 'Jamie Dean'),
(48, 2891, 6, 2),
(48, 2892, 1, 1062),
(48, 2892, 8, 'John Smith'),
(48, 2892, 6, 4)
;
INSERT INTO `staff_gf_entry_meta`
SELECT DISTINCT form_id, entry_id, 7 meta_key, '2018-12-17' meta_value
FROM testdb.staff_gf_entry_meta
;
Result:
# Number, Name, Hours Owed
'1062', 'John Smith', '4.29'
'1117', 'Jamie Dean', '2'
Start with something like below, and build from there.
$meta_labels = [
1 => 'ID'
8 => 'name'
6 => 'hours'
];
$output = [];
foreach($results as result) {
$eid = $result['entry_id'];
$label = $meta_labels[$result['meta_key']];
$output[$eid][$label] = $result['meta_value'];
}
And ideally the mapping between metadata IDs and their labels should be stored in the database, not hard-coded in the application.

how to display attendance report based on roll number and i want everything to be dynamic [duplicate]

This question already has an answer here:
MySQL pivot row into dynamic number of columns
(1 answer)
Closed 5 years ago.
I am working on Classes management project. Everything is done successfully but only stuck with attendance report work.
i want result like this
My table structure:
CREATE TABLE mp_attendence (atid int, atdisplayname varchar(9), atroll varchar(9), atstatus varchar(9), atdate date, atbatch varchar(9)) ;
INSERT INTO mp_attendence (atid, atdisplayname, atroll, atstatus, atdate, atbatch) VALUES (1, 'lavkush', '1', 'absent', '2017-03-01', 'p1'), (2, 'dhanji', '2', 'present', '2017-03-01', 'p1'), (3, 'lavkush', '1', 'present', '2017-03-02', 'p1'), (4, 'dhanji', '2', 'present', '2017-03-02', 'p1'), (5, 'lavkush', '1', 'present', '2017-03-04', 'p1'), (6, 'dhanji', '2', 'absent', '2017-03-04', 'p1'), (7, 'lavkush', '1', 'present', '2017-03-05', 'p1'), (8, 'dhanji', '2', 'absent', '2017-03-05', 'p1') ;
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'(case when atstatus = ''',
atstatus,
''' then (atstatus) end) AS ',
replace (DATE_FORMAT(atdate, '%d %M %Y'), ' ', '')
)
) INTO #sql
from mp_attendence;
SET #sql = CONCAT('SELECT atdisplayname, atroll, ', #sql, ' from mp_attendence
group by atroll');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
From above query i achieve this result but i want to combine same dates like 1st march result should be in same row.
result achieved

cakephp group by with n result for each group

I need to extract 3 result from each category in a database using cakephp, I tried to query in database using mysql session variable and conditions as below
SELECT *, #question_cat_rank := 0, #question_cat_country:=''
FROM
(SELECT *,
#question_cat_rank := IF(#question_cat_country = question_setname.question_cat, #question_cat_rank + 1, 1) AS question_cat_rank,
#question_cat_country := question_cat
FROM question_setname
ORDER BY question_cat desc
) ranked
WHERE question_cat_rank <= 3
its working fine in mysql query analyzer but when i query using cakephp its returning all results in table instaed of 3 results
i tried to create it using cakephp way like below
public $virtualFields = array(
'#catvirt' => 1,
'#catvirt2' => 'question_cat',
'incrmt' => 'IF(#catvirt2 = question_cat, #catvirt+1, 1)',
);
function getquestionGroup(){
$result1 = $this->find('all', array(
'fields' => array('question_cat', '#catvirt2', 'incrmt'),
'#catvirt2'=> 'question_cat',
'#catvirt' => '#catvirt'
)
);
$result = $this->find('all', array($result1,
'conditions' => array('#catvirt <=' => 3),
)
);
i tried to execute as prepared statementand stored procedure also still no luck its returning all results
any help will be appreciated
I would write that query this way:
SELECT x.*
FROM
( SELECT *
, #question_cat_rank := IF(#question_cat_country = question_cat, #question_cat_rank + 1, 1) question_cat_rank
, #question_cat_country := question_cat
FROM question_setname
, (SELECT #question_cat_rank := 0, #question_cat_country:='') vars
ORDER
BY question_cat DESC
) x
WHERE question_cat_rank <= 3;

Postgres to php Array

I have a table with values :
CREATE TABLE grade
(id int, name varchar(2), no int);
INSERT INTO grade
(id, name, no)
VALUES
(1, 'A', 7),
(2, 'B', 6),
(3, 'C', 10),
(4, 'D', 12),
(5, 'E', 15),
(6, 'F', 21),
(8, 'B', 16),
(7, 'F', 18),
(9, 'F', 25);
I need output in arrays to use.. i.e. :
[
['range','A','B','F'],
['0.00 - 4.41', 1, 1, 0],
['4.41 - 8.24', 0 , 1, 1]
...
...
['others', 0, 0, 1]
]
This is what I am tring (Fiddle) :
select range, array_agg(name) as name, array_agg(count) as count
from (
select case
when no between 0.00 and 4.41 then '0.00 - 4.41'
when no between 4.41 and 8.24 then '4.41 - 8.24'
when no between 8.24 and 14.77 then '8.24 - 14.77'
when no between 14.77 and 19.35 then '14.77 - 19.35'
when no between 19.35 and 23.00 then '19.35 - 23.00'
else 'Others' end as range, name, count (*) as count
from grade
WHERE name IN ('A','B','F')
group by range, name
order by name
) t
group by range
Is it possible to get desired output from db query only ? or I have use php to iterate through?
Found a way to archive result (Updated Fidddle):
select range, sum(a) as a_name,
sum(b) as b_name,
sum(f) as f_name,
sum(count) as total
from (
select case
when no between 0.00 and 4.41 then '0.00 - 4.41'
when no between 4.41 and 8.24 then '4.41 - 8.24'
when no between 8.24 and 14.77 then '8.24 - 14.77'
when no between 14.77 and 19.35 then '14.77 - 19.35'
when no between 19.35 and 23.00 then '19.35 - 23.00'
else 'Others' end as range,
count(name = 'A' OR NULL) as A,
count(name = 'B' OR NULL) as B,
count(name = 'F' OR NULL) as F,
count (*) as count
from grade
WHERE name IN ('A','B','F')
group by range, name
order by name
) t
group by range

Categories