Output concated value from mysql query - php

I am trying to output a concated value from my query but am having some issues.
Here is my query:
$result = mysql_query(
"SELECT c.company, n.nid, n.createdOn, CONCAT_WS(' ',c2.fname,c2.lname), CONCAT_WS(' ',c3.fname,c3.lname), n.urgent, n.description
FROM notes n
INNER JOIN Positions p ON FIND_IN_SET(p.id, n.forDepts) > 0
LEFT JOIN companies c ON c.userid = n.clientId
LEFT JOIN companies c2 ON c2.userid = n.createdBy
LEFT JOIN companies c3 ON c3.userid = n.claimedBy
GROUP BY n.nid
LIMIT 0,100"
);
My array is printing like so:
Array ( [0] => Honda of Kirkland [company] => Honda of Kirkland [1] => 1 [nid] => 1 [2] => 2009-09-28 21:33:15 [createdOn] => 2009-09-28 21:33:15 [3] => [CONCAT_WS(' ',c2.fname,c2.lname)] => [4] => [CONCAT_WS(' ',c3.fname,c3.lname)] => [5] => 0 [urgent] => 0 [6] => Milestones [description] => Milestones )
I am trying it like this, but it does not work:
while ($row = mysql_fetch_array($result)) {
$created_by = $row[3];
}

Related

Results different when running query in phpmyadmin and on website

I have two tables which I'm trying to run a query on.
If I run the below query in phpmyadmin I get the results I expect, just one result returned with id 9, but when running it on my webpage, its returning all 4 results. I can put what ever I want as the user id, even one that doesn't exist, its ignoring it.
$sql = "
SELECT c.*
, cu.userID
FROM ConversationUsers AS cu
LEFT
JOIN Conversations AS c
ON c.id = cu.convoID
WHERE cu.userID = userID
";
$q = $pdo->prepare($sql);
$q->bindValue(':userID', $vars['userID']);
$q->execute();
if($q->errorCode() != 0) {
$errors = $q->errorInfo();
echo($errors[2]);
}
foreach ($q->fetchAll() as $row) {
$convos[] = $row;
}
echo "<pre>";
print_r($convos);
echo "</pre>";
echo "
SELECT c.*
, cu.userID
FROM ConversationUsers AS cu
LEFT
JOIN Conversations AS c
ON c.id = cu.convoID
WHERE cu.userID = " . $vars["userID"];
The echo produces:
SELECT c.*
, cu.userID
FROM ConversationUsers AS cu
LEFT
JOIN Conversations AS c
ON c.id = cu.convoID
WHERE cu.userID = 1
The print produces
Array
(
[0] => Array
(
[id] => 9
[0] => 9
[lastUpdated] => 2019-12-29 00:00:00
[1] => 2019-12-29 00:00:00
[userID] => 1
[2] => 1
)
[1] => Array
(
[id] => 9
[0] => 9
[lastUpdated] => 2019-12-29 00:00:00
[1] => 2019-12-29 00:00:00
[userID] => 2
[2] => 2
)
[2] => Array
(
[id] => 10
[0] => 10
[lastUpdated] => 2019-12-29 00:00:00
[1] => 2019-12-29 00:00:00
[userID] => 2
[2] => 2
)
[3] => Array
(
[id] => 10
[0] => 10
[lastUpdated] => 2019-12-29 00:00:00
[1] => 2019-12-29 00:00:00
[userID] => 3
[2] => 3
)
)
The tables are setup as follows
CREATE TABLE `Conversations` (
`id` int(11) NOT NULL,
`lastUpdated` datetime NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `Conversations` (`id`, `lastUpdated`) VALUES
(9, '2019-12-29 00:00:00'),
(10, '2019-12-29 00:00:00');
CREATE TABLE `ConversationUsers` (
`convoID` int(11) NOT NULL,
`userID` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `ConversationUsers` (`convoID`, `userID`) VALUES
(9, 1),
(9, 2),
(10, 2),
(10, 3);
You're missing the : in :userID. That makes the WHERE always be true:
$sql = "SELECT c.*, cu.userID FROM ConversationUsers AS cu LEFT JOIN Conversations AS c ON c.id = cu.convoID WHERE cu.userID = :userID";
Your echo line used $vars["userID"] which is why it showed the correct result.
You miss the : before the parameter in your query:
$sql = "SELECT c.*, cu.userID FROM ConversationUsers AS cu LEFT JOIN Conversations AS c ON c.id = cu.convoID
WHERE cu.userID = :userID";

select GROUP_CONCAT in mysql

I have two tables:
table1:
id email
1 john#gmail.com
2 doe#gmail.com
table2:
userid key value
1 firstname john
1 phone 112233
2 firstname doe
2 phone 223344
This is mysql query without condition:
SELECT a.*,
b.*,
GROUP_CONCAT(b.key),
GROUP_CONCAT(b.value)
FROM table1 a
LEFT JOIN table2 b ON a.id = b.userid
This is result:
array(
[id] => 1
[email] => john#gmail.com
[userid] => 1
[key] => firstname,phone
[value] => john,112233
)
array(
[id] => 2
[email] => doe#gmail.com
[userid] => 2
[key] => firstname,phone
[value] => doe,223344
)
This is mysql query with condition:
SELECT a.*,
b.*,
GROUP_CONCAT(b.key),
GROUP_CONCAT(b.value)
FROM table1 a
LEFT JOIN table2 b ON a.id = b.userid
WHERE b.key = "firstname"
AND b.value LIKE '%jo%'
And this is result:
array(
[id] => 1
[email] => john#gmail.com
[userid] => 1
[key] => firstname
[value] => john
)
But I want this:
array(
[id] => 1
[email] => john#gmail.com
[userid] => 1
[key] => firstname,phone
[value] => john,112233
)
There any way to do this? thank for any help!
Your queries are lacking the GROUP BY clause to get a row per user. Then use a HAVING clause to make sure the aggregated row includes a firstname '%jo%':
SELECT a.*,
GROUP_CONCAT(b.key),
GROUP_CONCAT(b.value)
FROM table1 a
LEFT JOIN table2 b ON a.id = b.userid
GROUP BY a.id
HAVING sum(b.key = 'firstname'
AND b.value LIKE '%jo%') > 0;
true results in 1, false in 0 in MySQL. So checking whether the sum is greater than zero means checking whether the condition is true at least once.

Recursive function too slow for creating hierarchical select list

I have a query that gets me a Geo-Structure from the database.
Categories Table (30.000 rows inside):
id title parent type
-------------------------------
1 germany 0 1
2 bavaria 1 2
3 upper bavaria 2 3
4 munich 3 4
6 italy 0 1
7 toscana 6 2
8 city florence 7 3
9 florence 8 4
Categories Language Table
cid language title
--------------------------
1 en-UK germany
2 de-DE deutschland
Objects table:
id title landid regionid uregionid cityid
--------------------------------------------------
1 o1 1 2 3 4
2 o2 1 2 3 4
3 o3 6 7 8 9
MySQL query:
SELECT c.id, c.title, l.title AS translated, c.type, c.parent, count(c.id) as cnt
FROM category c
LEFT JOIN objects o ON (o.landid = c.id OR o.regionid = c.id OR o.uregionid = c.id OR o.cityid = c.id)
LEFT JOIN category_lang l ON l.cid = c.id AND l.language = "en-UK"
WHERE c.published = 1 AND o.published = 1
GROUP BY c.id
ORDER BY c.parent
I get an associative array ($tree) with values like here:
Array
(
[0] => Array
(
[id] => 1
[title] => Germany
[type] => 1
[parent] => 0
[cnt] => 1
)
[1] => Array
(
[id] => 6
[title] => Italy
[type] => 1
[parent] => 0
[cnt] => 1
)
[2] => Array
(
[id] => 2
[title] => Bavaria
[type] => 2
[parent] => 1
[cnt] => 1
)
[3] => Array
(
[id] => 7
[title] => Toscana
[type] => 2
[parent] => 6
[cnt] => 1
)
[4] => Array
(
[id] => 3
[title] => Upper Bavaria
[type] => 3
[parent] => 2
[cnt] => 1
)
[5] => Array
(
[id] => 8
[title] => City Florence
[type] => 3
[parent] => 7
[cnt] => 1
)
[6] => Array
(
[id] => 4
[title] => Munich
[type] => 4
[parent] => 3
[cnt] => 1
)
[7] => Array
(
[id] => 9
[title] => Florence
[type] => 4
[parent] => 8
[cnt] => 1
)
)
Then i create a structure that will prepare the display of a select list:
public static function buildTree($tree, $root = 0) {
$return = array();
foreach($tree as $child) {
if($child['parent'] == $root) {
$return[] = array(
'name' => $child,
'next' => self::buildTree($tree, $child['id'])
);
}
}
return empty($return) ? null : $return;
}
Then i send the structure inside $return to a function to create the final select list:
public static function buildSelect($tree, $s) {
$option = '';
if(!is_null($tree) && count($tree) > 0) {
foreach($tree as $node) {
$selected = '';
$class_type = $node['name']['type'];
$option .= '<option value="'.$node['name']['id'].'" class="h'.$class_type.'" '.$selected.' data-type="'.$node['name']['type'].'">'
.$node['name']['title']. ' (' . $node['name']['cnt'] . ')</option>'
. self::buildSelect($node['next'], $s);
}
return $option;
}
}
This all works but if the Geo-Structure gets really big the db query gets terrible slow.
Would appreciate any ideas about how to speed this up, thanks!
The basics seem there, and without setting up a lot of test data it is difficult to do any testing.
The following has a couple of minor tweaks (you will need to change to cope with where you want to keep the compare routine). Possibly not enough to fix the issue. However I would be interested to know what effect it has, and also some timings of which method is taking the time, and how it ramps up:-
<?php
function cmp($a, $b)
{
return strcmp($a["parent"], $b["parent"]);
}
usort($tree, "cmp");
$recursive_tree = self::buildTree($tree);
echo self::buildSelect($recursive_tree, '');
public static function buildTree(&$tree, $root = 0)
{
$return = array();
foreach($tree as $child)
{
if($child['parent'] == $root)
{
$return[] = array(
'name' => $child,
'next' => self::buildTree($tree, $child['id'])
);
}
else
{
if ($child['parent'] > $root)
{
break;
}
}
}
return empty($return) ? null : $return;
}
public static function buildSelect(&$tree, $s)
{
$option = '';
if(!is_null($tree) && count($tree) > 0)
{
foreach($tree as $node)
{
$selected = '';
$class_type = $node['name']['type'];
$option .= '<option value="'.$node['name']['id'].'" class="h'.$class_type.'" '.$selected.' data-type="'.$node['name']['type'].'">'
.$node['name']['title']. ' (' . $node['name']['cnt'] . ')</option>'
. self::buildSelect($node['next'], $s);
}
return $option;
}
}
Does the output have to be built up, or can be be directly output (even to a temp file)?
EDIT
If your objects table has indexes on landid, regionid, uregionid and cityid (ie, separate indexes on each one) then try this:-
SELECT c.id, c.title, c.type, c.parent, count(c.id) as cnt
FROM category c
LEFT JOIN objects o1 ON o1.landid = c.id AND o1.published = 1
LEFT JOIN objects o2 ON o2.regionid = c.id AND o2.published = 1
LEFT JOIN objects o3 ON o3.uregionid = c.id AND o3.published = 1
LEFT JOIN objects o4 ON o4.cityid = c.id AND o4.published = 1
WHERE c.published = 1
AND (01.id IS NOT NULL
OR 02.id IS NOT NULL
OR 03.id IS NOT NULL
OR 04.id IS NOT NULL)
GROUP BY c.id,
c.title,
c.type,
c.parent
ORDER BY c.parent
EDIT
Adding in your language table:-
SELECT c.id,
c.title,
l.title AS translated,
c.type,
c.parent,
count(c.id) as cnt
FROM category c
LEFT OUTER JOIN category_lang l ON l.cid = c.id AND l.language = "en-UK"
LEFT OUTER JOIN objects o1 ON o1.landid = c.id AND o1.published = 1
LEFT OUTER JOIN objects o2 ON o2.regionid = c.id AND o2.published = 1
LEFT OUTER JOIN objects o3 ON o3.uregionid = c.id AND o3.published = 1
LEFT OUTER JOIN objects o4 ON o4.cityid = c.id AND o4.published = 1
WHERE c.published = 1
AND (01.id IS NOT NULL
OR 02.id IS NOT NULL
OR 03.id IS NOT NULL
OR 04.id IS NOT NULL)
GROUP BY c.id,
c.title,
translated,
c.type,
c.parent
ORDER BY c.parent
As to whether this or the UNION solution is better, that will come down largely to your data. For example this solution will struggle if the 4 id columns in the objects table can have duplicates (unlikely, as I would doubt a city can also be a region).
Take a look at Nested Sets. It is fairly easy to add calculated tree_left, tree_right, and tree_depth to your current parent-based model — parent stays as primary data, other three will be recalculated on change.
Then, you can use a much simpler select to get items for the whole subtree of categories and thanks to the tree_depth column it is easy to calculate correct indentation in your <select> element or in listings without any recursion.

How to get the count of each category using group by?

I am using cakephp 2.0 framework. I want to count the number of records of each category.
Here i have two tables
eve_appointment_icons ->having columns- appointment_icon_id,appointment_icon_type_name
eve_appointments ->having columns- id, appointment_icon_id
I have fetch this data using this query:-
$this->EveAppointment->query("SELECT
a.appointment_type
d.appointment_icon_id,
a.appointment_icon_id ,
d.appointment_icon_type_name,
FROM eve_appointments a,
eve_appointment_icons d
WHERE a.appointment_type =1
AND d.appointment_icon_id = a.appointment_icon_id
UNION
SELECT
a.appointment_type
d.appointment_icon_id,
a.appointment_icon_id ,
d.appointment_icon_type_name,
FROM eve_appointments a,
eve_appointment_icons d
WHERE a.appointment_type =2
AND d.appointment_icon_id = a.appointment_icon_id
UNION
SELECT
a.appointment_type
d.appointment_icon_id,
a.appointment_icon_id ,
d.appointment_icon_type_name,
FROM eve_appointments a,
eve_appointment_icons d
WHERE a.appointment_type =3
AND d.appointment_icon_id = a.appointment_icon_id");
Here when I print this:
Array
(
[0] => Array
(
[appointment_icon_id] => 1
[appointment_icon_type_name] => Yoga
)
[1] => Array
(
[appointment_icon_id] => 1
[appointment_icon_type_name] => Yoga
)
[2] => Array
(
[appointment_icon_id] => 2
[appointment_icon_type_name] => Visit
)
[3] => Array
(
[appointment_icon_id] => 3
[appointment_icon_type_name] => Physio Therapy
)
[4] => Array
(
[appointment_icon_id] => 4
[appointment_icon_type_name] => Diagnostic
)
[5] => Array
(
[appointment_icon_id] => 4
[appointment_icon_type_name] => Diagnostic
)
[6] => Array
(
[appointment_icon_id] => 4
[appointment_icon_type_name] => Diagnostic
)
)
Now you can see the Yoga having appointment_icon_id is 1, visit having appointment_icon_id 2 and vice versa for others.
I want count of all these categories.
Like:
appointment_icon_type_name count
Yoga 2
Visit 1
Physio Therapy 1
Diagnostic 3
All you need is this:
select d.appointment_icon_type_name, count(*)
FROM eve_appointments a,
eve_appointment_icons d
WHERE d.appointment_icon_id = a.appointment_icon_id
group by d.appointment_icon_type_name

SQL group inner join cannot get all results

I really trying to get this SQL to work. Im no expert so really cant figure this out.
$sqlquery = " SELECT
s.searchword AS searchword,
s.id AS id,
COUNT( c.id ) AS searchresult,
s.region AS region
FROM search_words AS s
INNER JOIN company_data AS c ON
c.branch_text LIKE CONCAT( '%', s.searchword, '%' )
GROUP BY 1 ORDER BY s.date DESC";
This gives me:
Array
(
[0] => Array
(
[searchword] => WHOLESALE
[searchid] => 427
[searchresult] => 98
[region] => stockholm
)
[1] => Array
(
[searchword] => cars
[searchid] => 426
[searchresult] => 26
[region] =>
)
[2] => Array
(
[searchword] => Retail
[searchid] => 342
[searchresult] => 41
[region] => stockholm
)
[3] => Array
(
[searchword] => Springs
[searchid] => 339
[searchresult] => 4
[region] => stockholm
)
[4] => Array
(
[searchword] => Leasing
[searchid] => 343
[searchresult] => 2
[region] => stockholm
)
[5] => Array
(
[searchword] => Food
[searchid] => 340
[searchresult] => 37
[region] => stockholm
)
)
But, it does not give me any of the other results where there are no searchhits, would retur something like [searchresult] => 0. Meaning they do not group as I wish, because there are no such searchwords within the company_data table.
How can I fix this, please help :(
EDIT:
Here is the full code:
public function getUserSearches()
{
$sqlquery = " SELECT
s.searchword AS searchword,
s.id AS id,
s.userId AS userid,
COUNT( c.id ) AS searchresult,
s.region AS region
FROM search_words AS s
INNER JOIN company_data AS c ON
c.branch_text LIKE CONCAT( '%', s.searchword, '%' )
GROUP BY 1 ORDER BY s.date DESC";
// IS THERE ANYTHING WRONG HERE?? LIKE IT DOES NOT MATCH AGAINST THE USER?
$result = $this->dbh->query($sqlquery, array(":userId" => $this->user_id));
$arr = array();
foreach ($result as $item) {
array_push($arr, array('searchword' => $item['searchword'], 'searchid' => $item['id'],
'searchresult' => $item['searchresult'], 'userid' => $item['userid'],
'region' => $item['region']));
}
return json_encode($arr);
return print_r($arr);
}
Use LEFT JOIN so that any row that doesn't match the search criteria in the join condition will come with null, therefore count will be 0. Something like this:
SELECT
s.searchword AS searchword,
s.id AS id,
s.region AS region,
COUNT(COALESCE(c.id, 0)) AS searchresult
FROM search_words AS s
LEFT JOIN company_data AS c
ON c.branch_text LIKE CONCAT( '%', s.searchword, '%' )
GROUP BY s.searchword, s.id, s.region
ORDER BY s.date DESC;
See this for more details about the SQL Join types.
You need a LEFT JOIN, instead of an INNER JOIN. However, there are other simplifications you can make as well:
SELECT s.searchword, s.id, COUNT( c.id ) AS searchresult, s.region
FROM search_words s INNER JOIN
company_data c
ON c.branch_text LIKE CONCAT('%', s.searchword, '%')
GROUP BY 1
ORDER BY s.date DESC
For instance, you don't need column aliases when you are not changing the column name. You are only aggregating on searchword; I assume this is unique in search_words. Otherwise, s.id, s.region. and s.date would have indeterminate values.
The use of LIKE for the JOIN affects performance. If you only have a small amount of data, this is fine. Otherwise, you might want to think about other data structures.
I think what you want is perhaps this:
public function getUserSearches()
{
$sqlquery = " SELECT
s.searchword AS searchword,
s.id AS id,
s.userId AS userid,
COUNT( c.id ) AS searchresult,
s.region AS region
FROM search_words AS s
WHERE s.userId='"+$this->userid+"'
LEFT JOIN company_data AS c ON
c.branch_text LIKE CONCAT( '%', s.searchword, '%' )
GROUP BY 1 ORDER BY s.date DESC";
// IS THERE ANYTHING WRONG HERE?? LIKE IT DOES NOT MATCH AGAINST THE USER?
$result = $this->dbh->query($sqlquery);
$arr = array();
foreach ($result as $item) {
array_push($arr, array('searchword' => $item['searchword'], 'searchid' => $item['id'],
'searchresult' => $item['searchresult'], 'userid' => $item['userid'],
'region' => $item['region']));
}
return json_encode($arr);
return print_r($arr);
}

Categories