Zendframework Union Issue - php

I have this code running
$sq = $this->_codes->getAdapter()->select()
->from (array('cs' => 'code_statuses'), array('total' => 'count(*)'))
->join (
array ('c' => 'codes'), 'c.code_id = cs.code_id',
array ('human_state' => new Zend_Db_Expr("CASE c.state_id WHEN 3 THEN 'active' WHEN 5 THEN 'suspended' ELSE 'inactive' END"), 'c.*')
)
->group('cs.code_id');
$sqtemp = $this->_codes->getAdapter()->select()
->from (array('cs' => 'code_statuses'), array('total' => 'count(*)'))
->join (
array ('c' => 'codes'), 'c.code_id = cs.code_id',
array ('human_state' => new Zend_Db_Expr("CASE c.state_id WHEN 3 THEN 'active' WHEN 5 THEN 'suspended' ELSE 'inactive' END"), 'c.*')
)
->group('cs.code_id');
if (!empty($options['state_id'])):
if (is_array($options['state_id'])):
$states = 'cs.state_id=' . implode(' OR cs.state_id=', $options['state_id']);
$sq->where($states)
->having(total<=4);
$sqtemp->where ('cs.state_id=5')
->having(total<4);
else:
$sq->where ('cs.state_id=?', $options['state_id']);
endif;
The issue occurs when i try to use union
$sqfinal=$this->_codes->getAdapter()->select()
->union(array($sq,$sqtemp))
->order('cs.code_id');
but individually $sq and $sqtemp work fine
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'cs.code_id' in 'order clause'
Not sure where I am going wrong
Any help will be appreciated
*edit
SELECT count(*) AS `total`,
CASE c.state_id
WHEN 3 THEN 'active'
WHEN 5 THEN 'suspended'
ELSE 'inactive'
END AS `human_state`, `c`.*
FROM `code_statuses` AS `cs`
INNER JOIN `codes` AS `c`
ON c.code_id = cs.code_id
WHERE (cs.state_id=1 OR cs.state_id=2 OR cs.state_id=4)
GROUP BY `cs`.`code_id` HAVING (total<=4)
UNION
SELECT count(*) AS `total`,
CASE c.state_id
WHEN 3 THEN 'active'
WHEN 5 THEN 'suspended'
ELSE 'inactive'
END AS `human_state`, `c`.*
FROM `code_statuses` AS `cs`
INNER JOIN `codes` AS `c`
ON c.code_id = cs.code_id
WHERE (cs.state_id=5)
GROUP BY `cs`.`code_id`
HAVING (total<4)
The part before the union is $sq, the part afterwards is $sqtemp, the combination of the two gives the print out above
Both of them with union in is the whole thing

After a second look at your code, I suspect the oder() call on the union. You're ordering by cs.code_id, whic is not mentioned in any of the select statements, nor is the c.code_id for that matter.
Try adding either c.code_id or cs.code_id to the SELECT't that make up the UNION, possibly consider using an alias, which you can then use in your order clause.
$sq = $this->_codes->getAdapter()->select()
->from(array('cs' => 'code_statuses'),
array(
'total' => 'count(*)'
'cscodeids' => 'code_ids',
));
//...
$union = $this->_codes->getAdapter()
->select()
->union(array($sq,$sqtemp))
->order('cscodeids');
This, I believe, should work. I've taken inspiration from various places. Here are some of the links that lead up to my answer (can't find all of them ATM):
MySQL union and order by help
Every derived table must have its own alias - error from combination descending MySQL
Using union and order by clause in mysql
How to use union in zend db
Zend_Db union issue: old bug report... contains some details

Related

Can I do a treatement of returning values directly in SQL query

I was wondering if it was possible for a SQL query to return datas using some entries in the SQL. I'm sorry if it's not explicite (not english here, i guess you figured it out), so let's see an example :
$req = $bdd->prepare('SELECT u.u_id, u.u_name, um.um_id, um.um_name, um.um_value
FROM users as u
LEFT JOIN users_metas as um ON um.um_uid = u.u_id');
$prepare->execute();
$datas = $prepare->fetchAll();
So here, i'm gonna have in $datas an array like
$datas = array(
0 =>
u_id => 1
u_name => name
um_id => 1
um_name => meta_name1
um_value => meta_value1
1 =>
u_id => 1
u_name => name
um_id => 2
um_name => meta_name2
um_value => meta_value2
...
);
What I would like it's something like :
$datas = array(
0 =>
u_id => 1
u_name => name
meta_name1 => meta_value1
meta_name2 => meta_value2
1 =>
u_id => an other user
...
);
I already have this after I manually take care of $datas, but i was wondering if I could reach this presentation with no treatement, only with SQL ?
I hope you guys will understand me, I'll do my best to be explicite if you have some questions.
Regards.
MySQL solution
Transforming a table-valued expression into a table by converting unique values from one column in the table into multiple columns in the output is known as a pivot. Unlike some other DBMSes, MySQL doesn't have a PIVOT or CROSSTAB function so you can't write a one size fits all solution to this problem.
If you know all the possible values of um_name in advance, you can emulate this behaviour using aggregate functions.
SELECT u.u_id, u.u_name,
MAX(CASE WHEN um_name = 'meta_name1' THEN um_value END) meta_name1,
MAX(CASE WHEN um_name = 'meta_name2' THEN um_value END) meta_name2
FROM users as u
LEFT JOIN users_metas as um ON um.um_uid = u.u_id
GROUP BY u.u_id, u.u_name;
The downside of this solution is that you have to keep adding MAX(CASE WHEN um_name = '...' THEN um_value END) columns to the query for every possible value of um_name.
In theory you could generate this query in PHP by selecting all the distinct um_name values first, but this wouldn't make your code simpler or faster so there's not a lot of point.
PHP solution
As an alternative, it's only a few lines of PHP to convert your current output from fetchAll() into the format you desire.
$out = [];
array_walk($datas, function($v) use(&$out) {
$out[$v["u_id"]] = (isset($out[$v["u_id"]]) ? $out[$v["u_id"]] : [])
+ ["u_id" => $v["u_id"],
"u_name" => $v["u_name"],
$v["um_name"] => $v["um_value"]];
});
$out = array_values($out);
This doesn't set the keys if the record doesn't exist in the users_metas table, so you'll need to check with isset before accessing a particular value if it isn't present for all users.

create zf2 query 'not in' clause using uninon

I want to use union queries result in Not in in ZF2. Result of this query will return
Query: $selectLevelTwoPointFive->combine($selectLevelAll, 'UNION');
Result: ( SELECT `assigned`.`item_id` AS `item_id` FROM `assign_items_level_twopointfive` AS `assigned` ) UNION ( SELECT `assigned`.`item_id` AS `item_id` FROM `assign_items` AS `assigned` )
Now I want to use this query's result in 'not in' clause.
$select = $sql->select()->from(array(
"items" => "cu_items"
))->columns(array('item_name'=> new \Zend\Db\Sql\Expression(" group_concat(`items`.`item_name`)") ));
$select->join(array(
"ca" => "cu_areas"
), new Expression(" ca.area_id = items.area_id ")
, array(
'area_name'
), $selectLevelAll::JOIN_INNER);
$select->where->addPredicate(new \Zend\Db\Sql\Predicate\Expression('items.item_id NOT IN (?)',
array($selectLevelTwoPointFive)));
The result is:
SELECT group_concat(`items`.`item_name`) AS `item_name`, `ca`.`area_name` AS `area_name` FROM `cu_items` AS `items` INNER JOIN `cu_areas` AS `ca` ON ca.area_id = items.area_id WHERE `items`.`is_opted` = 'yes' AND `ca`.`is_opted` = 'yes' AND items.item_id NOT IN ((( SELECT `assigned`.`item_id` AS `item_id` FROM `assign_items_level_twopointfive` AS `assigned` ) UNION ( SELECT `assigned`.`item_id` AS `item_id` FROM `assign_items` AS `assigned` )))
It is showing mysql error :
Error Code: 1064. You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'UNION ( SELECTassigned.item_idASitem_idFROMassign_itemsASassigne' at line 1`
My required query is:
SELECT group_concat(items.item_name) AS item_name, ca.area_name AS area_name FROM cu_items
AS items INNER JOIN cu_areas AS ca ON ca.area_id = items.area_id
WHERE items.is_opted = 'yes' AND ca.is_opted = 'yes' AND items.item_id NOT IN
(
(
SELECT assigned.item_id AS item_id FROM assign_items_level_twopointfive AS assigned
UNION ( SELECT assigned.item_id AS item_id FROM assign_items AS assigned )
)
)
Actually when i use combine() it adds round bracket (first query) union (second query) But in not I need first query union second query.
Please help. If any one suggest the mysql changes it also fine.
Thanks God, Find the page.
We need to override the setSpecification()
Here is the link for the answer.
we can override the query output

Laravel 4 union generate wrong SQL

Here is the code:
$f = DB::table("topics")
->join("recommends", "topics.id", "=", "recommends.courseid")
->where("recommends.re_type", "=", $re_type)
->where("recommends.re_location", "=", $re_location)
->orderBy("recommends.weigh", "desc");
$s = DB::table("topics")
->orderBy("topics.create_time", "desc");
$f->union($s)->get();
I got a wrong SQL around key word union:
select * from `topics` inner join `recommends`
on `topics`.`id` = `recommends`.`courseid`
where `recommends`.`re_type` = ?
and `recommends`.`re_location` = ?
order by `recommends`.`weigh` desc
union //here!!!!!
select * from `topics` order by `topics`.`create_time` desc
The error info:
SQLSTATE[HY000]: General error: 1221
Incorrect usage of UNION and ORDER BY (SQL: ...)
(Bindings: array ( 0 => 3, 1 => 7, ))
What is the problem?
MySQL UNIONs expect the same columns in all statements. Because you're joining another table in $f, the columns between the two statements don't match.
See MySql SELECT union for different columns?
In this case, it might be less of a headache to use the PDO object directly.
$pdo = DB::connection()->getPdo();
Found another problem with your query. You should relocate your first order by clause:
->orderBy("recommends.weigh", "desc");
It is producing the order by you before union and MySQL will not accept that.

Mistake in my CodeIgniter sql join query

So I have been lookin for mistake for a while, but still can't find it.
Here is the code -
$this->db->select('*');
$this->db->from('friendRequests');
$this->db->where(array('friendRequests.status' => 1, 'users.status' => 1));
$this->db->or_where(array('friendRequests.senderId' => $this->session->userdata('userId'), 'friendRequests.receiverId' => $this->session->userdata('userId')));
$this->db->join('users', 'users.id = '.$this->session->userdata('userId'));
$query = $this->db->get();
It provides me this error -
Unknown column '1' in 'on clause'
SELECT *
FROM (`friendRequests`)
JOIN `users` ON `users`.`id` = `1`
WHERE `friendRequests`.`status` = 1
AND `users`.`status` = 1
OR `friendRequests`.`senderId` = '1'
OR `friendRequests`.`receiverId` = '1'
If an entry is surrounded in backticks it counts as a column even if it would not be one without the backtics. It thinks that 1 is a column on the JOIN line because of this.
Apparently this is a product of the join method in CI. You can fix it very easily by moving that condition to the WHERE clause. There's no need for it to be in the JOIN clause.
JOIN does not work that way; the syntax is:
JOIN 'tablename' ON 'tablename.field' = 'othertable.field'
If I had to assume you were trying to get a user's friend requests:
JOIN 'users' ON 'users'.'id' = 'friendRequests'.'receiverId'

MySQL LEFT JOIN where joined table is null

My current SQL is:
SELECT * FROM `node`
LEFT JOIN `field_data_field_area_of_study_category`
ON `field_data_field_area_of_study_category`.`entity_id` = `node`.`nid`
WHERE `node`.`type` = 'area_of_study'
GROUP BY `node`.`nid`
For now, to make it work I run a foreach loop to is_null() on the column.
I'm trying to emulate the is_null() check within the SQL query.
Thanks for reading.
EDIT
This is the foreach loop that runs the is_null(). I think a better way to ask my question would be: how do make the SQL return only rows from the node table where there is no matching field_data_field_area_of_study_category table?
foreach($aos_nodes as $aos_node):
if (is_null($aos_node->field_area_of_study_category_tid)):
$menu_item_display[$aos_node->title] = array(
'id' => $aos_node->nid,
'type' => 'node',
'children_markup' => '',
'icon' => '',
'name' => $aos_node->title,
'href' => drupal_get_path_alias('node/'.$aos_node->nid),
);
endif;
endforeach;
To return the rows where there isn't a matching one:
SELECT * FROM `node`
LEFT JOIN `field_data_field_area_of_study_category`
ON `field_data_field_area_of_study_category`.`entity_id` = `node`.`nid`
WHERE `node`.`type` = 'area_of_study'
and `field_data_field_area_of_study_category`.`entity_id` is null
GROUP BY `node`.`nid`
Additionally, you can use not exists:
select
*
from
node n
where
n.type = 'area_of_study'
and not exists (
select
1
from
field_data_field_area_of_study_category f
where
f.entity_id = n.nid
)

Categories