MySQL find multiple records by two columns - php

Let's say I have an array as follows (ID => Type):
$contentIndexes = [
32 => 'news',
40 => 'blog',
68 => 'blog',
109 => 'document',
124 => 'news'
]
And the following database table:
CREATE TABLE `ContentIndex` (
`ID` INT(11) NOT NULL AUTO_INCREMENT,
`ItemID` INT(11) NOT NULL,
`ItemType` VARCHAR(50) NOT NULL COLLATE 'utf8_unicode_ci',
//...
);
How would I retrieve each ContentIndex based on the combination of the 'ItemID' and 'ItemType' columns (preferably with just one query).
Using WHERE IN is not an option since it wouldn't take the combination in consideration:
ContentIndexQuery::create()
->filterByItemID(array_keys($contentIndexes))
->filterByItemType($contentIndexes)
->find();
Any ideas?

I don't know the Propel syntax, but the basic SQL syntax would be with OR.
WHERE ((ItemID = 32 AND ItemType = 'news')
OR
(ItemID = 40 AND ItemType = 'blog')
OR
(ItemID = 68 AND ItemType = 'blog')
OR
(ItemID = 109 AND ItemType = 'document')
OR
(ItemID = 124 AND ItemType = 'news')
)

For any Propel users encountering this problem in the future, this is what I came up with to create a query as Barmar stated:
$items = ContentIndexQuery::create();
$i = 0;
foreach ($contentIndexes as $id = > $type) {
if ($i > 0) $items->_or();
$items->condition('cond1'.$i, ContentIndexTableMap::COL_ITEM_ID . ' = ?', $id)
->condition('cond2'.$i, ContentIndexTableMap::COL_ITEM_TYPE . ' = ?', $type)
->where(['cond1'.$i, 'cond2'.$i], 'AND');
$i += 1;
}
$items = $items->find()->getData();

Related

Filter and sort in gridview with junction table in yii2

I have 3 tables:
TABLE projects (
id SERIAL PRIMARY KEY,
name text NOT NULL,
);
TABLE project_participants (
id SERIAL PRIMARY KEY,
proj_id integer REFERENCES projects (id) NOT NULL,
);
TABLE valuations (
id SERIAL PRIMARY KEY,
proj_partic_id integer REFERENCES project_participants (id) NOT NULL,
tehn_knowledge numeric NOT NULL,
comunication_skills numeric NOT NULL,
);
and functions in Projects model:
public function getValuations() {
return $this->hasMany(Valuations::className(), ['proj_partic_id' => 'id'])
->viaTable('project_participants', ['proj_id' => 'id']);
}
public function getValuationsSum() {
$sum = 0;
foreach ($this->valuations as $val){
$sum += $val->tehn_knowledge;
$sum += $val->comunication_skills;
}
return $sum;
}
So in gridview I can show summary data like:
'columns' => [
'valuationsSum',
But now I want to search and filter values, but can't write working query in projectsSearch model. :( Tried:
$query = Projects::find();
$subQuery = Valuations::find(Valuations::className(), ['proj_partic_id' => 'id'])
->viaTable('project_participants', ['proj_id' => 'id'])
->select('proj_partic_id, SUM(tehn_knowledge + comunication_skills) as rating')
->groupBy('project_participants.id');
$query->leftJoin([
'valuation'=>$subQuery
]);
and after $this->load($params);
added
$query->andFilterWhere([
'valuation.rating' => $this->valuationsSum,
But seems this $subQuery is absolutely wrong. Tried to write it similar to example here, but I have this junction table.

Error 1064 <42000> on mysql when selecting using double quoted strings

The query produced by Zend Framework 2:
SELECT "uc".*, "c".* FROM "user_contacts" AS "uc" INNER JOIN "contacts" AS "c" ON "uc"."contact_id" = "c"."contact_id" WHERE "uc"."user_id" = '2' AND "c"."user_id" = '1';
results in this error (when run on command line):
ERROR 1064 (42000): 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 '.*, "c".* FROM "user_contacts" AS "uc" INNER JOIN "contacts" AS "c" ON "uc"."con' at line 1
This query (exact same query minus double quotes) runs fine:
SELECT uc.*, c.* FROM user_contacts AS uc INNER JOIN contacts AS c ON uc.contact_id = c.contact_id WHERE c.user_id = 2 AND uc.user_id = 1;
+---------+------------+------------+---------+
| user_id | contact_id | contact_id | user_id |
+---------+------------+------------+---------+
| 1 | 7 | 7 | 2 |
+---------+------------+------------+---------+
1 row in set (0.00 sec)
Why is this so and how can I fix this?
Using AMP stack on Ubuntu 12.10.
Tables look like so:
CREATE TABLE `contacts` (
`contact_id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
PRIMARY KEY (`contact_id`),
UNIQUE KEY `contact_id_UNIQUE` (`contact_id`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8
CREATE TABLE `user_contacts` (
`user_id` int(11) NOT NULL,
`contact_id` int(11) NOT NULL,
PRIMARY KEY (`user_id`,`contact_id`),
KEY `user_contacts_user_id_fkey_idx` (`user_id`),
KEY `user_contacts_contact_id_idx` (`contact_id`),
CONSTRAINT `user_contacts_contact_id_fkey` FOREIGN KEY (`contact_id`) REFERENCES `contacts` (`contact_id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `user_contacts_user_id_fkey` FOREIGN KEY (`user_id`) REFERENCES `user` (`user_id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8
Zend db adapter code:
return array(
'db' => array(
'driver' => 'Pdo',
'dsn' => 'mysql:dbname=' . $dbName . ';host=' . $host,
'driver_options' => array(
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\''
),
),
'service_manager' => array(
'factories' => array(
'Zend\Db\Adapter\Adapter'
=> 'Zend\Db\Adapter\AdapterServiceFactory',
),
),
);
Select code:
public function checkIfFriends($currentUserId,$requestedUserId) {
$currentUserId = (int) $currentUserId;
$requestedUserId = (int) $requestedUserId;
$sql = new Sql($this->tableGateway->getAdapter());
$select = $sql->select();
$select->from(array('uc' => $this->tableGateway->getTable()))
->join(array('c' => 'contacts'), 'uc.contact_id = c.contact_id');
$where = new Where();
$where
->equalTo('uc.user_id', $currentUserId)
->equalTo('c.user_id', $requestedUserId);
$select->where($where);
//echo $select->getSqlString();
$rowSet = $this->tableGateway->selectWith($select);
$row = $rowSet->current();
return ($row) ? true: false;
}
Why has this been closed as a exact duplicate? it isn't. I understand the problem might be the same but ZF2 is producing a query which doesn't run due to the quoting.
Solution as found in this Zend Framework forum post:
Works but is not really an acceptable solution, a better solution would be much appreciated.
public function checkIfFriends($currentUserId,$requestedUserId) {
$currentUserId = (int) $currentUserId;
$requestedUserId = (int) $requestedUserId;
$sql = new Sql($this->tableGateway->getAdapter());
$select = $sql->select();
$select->from(array('uc' => $this->tableGateway->getTable()))
->join(array('c' => 'contacts'), 'uc.contact_id = c.contact_id');
$where = new Where();
$where
->equalTo('uc.user_id', $currentUserId)
->equalTo('c.user_id', $requestedUserId);
$select->where($where);
$dbAdapter = $this->tableGateway->getAdapter();
$string = $sql->getSqlStringForSqlObject($select);
$rowSet = $dbAdapter->query($string, $dbAdapter::QUERY_MODE_EXECUTE);
$row = $rowSet->current();
return ($row) ? true: false;
}
I solved this problem by turning off the dead key settings in the OS.
How to do this in Linux Mint: http://forums.linuxmint.com/viewtopic.php?f=55&t=97812

MySQL Select FROM 3 tables AND put that in PHP array

Sorry for bad english and bad title!
I have the table "post"
id title
1 test Thread
2 hello
3 just
so have "tags"
tagid tagname
1 test
2 russia
3 new site
so have a post_tags
tagid postid
1 1
2 1
3 1
I need an array from var_dump next below:
$posts = array(
1 => array(
'title' => 'test Thread',
'tags' => array(
'test', 'russia', 'new site',
),
),
2 => array(
'title' => 'hello',
'tags' => NULL
),
3 => array(
'title' => 'just',
'tags' => NULL
),
)
I trying do it, but i getting not that what i want.
SELECT `post`.`id`, `post`.`title`, `tags`.`tagname` FROM `post`
LEFT JOIN `post_tags` ON `post_tags`.`tagid` = `post`.`id`
LEFT JOIN `tags` ON `post_tags`.`tagid` = `tags`.`tagid`
I getting in SQL next following:
id title tagname
1 test Thread test
1 test Thread russia
1 test Thread newsite
2 hello NULL
3 just NULL
PHP
$query = mysql_query("SELECT `post`.`id`, `post`.`title`, `tags`.`tagname` FROM `post`
LEFT JOIN `post_tags` ON `post_tags`.`tagid` = `post`.`id`
LEFT JOIN `tags` ON `post_tags`.`tagid` = `tags`.`tagid`");
$posts = array();
while ($row = mysql_fetch_assoc($query))
{
$posts[] = $row;
}
var_dump($posts);
Thank you!!!
The query is fine. You just need some logic in your loop:
while ($row = mysql_fetch_assoc($query))
{
if (isset($posts[$row['id']])) {
$posts[$row['id']]['tags'][] = $row['tagname'];
}
else {
$posts[$row['id']] = array(
'title' => $row['title'],
'tags' => $row['tagname'] === null ? null : array($row['tagname'])
);
}
}
If you have already seen a row with the same post id then all you want from the current row is the tag name (so add this to the "tags" array). If it's the first time a row with this post id is seen just add it to $posts, being a little careful to set "tags" to either null or an array with one element.
you cannot get a multi-dimensional arrays back from a mysql database. you must do your own post processing to the results if you want it in that form. Something like this maybe?
$posts = array();
while ($row = mysql_fetch_assoc($query))
{
if (!isset($posts[$row['id']])) {
$posts[$row['id']] = array();
$posts[$row['id']]['title'] = $row['title'];
$posts[$row['id']]['tags'] = array();
}
if ($row['tagname'] != null) $posts[$row['id']]['tags'][] = $row['tagname'];
}
Try this:
while ($row = mysql_fetch_assoc($query))
{
if( !isset( $posts[$row["id"]] ) ) {
$posts[ $row["id"] ] = array( "title" => $row["title"], "tags" => array() );
}
array_push( $posts[ $row["id"] ][ "tags" ], $row["tagname"] );
}
I can't debug it, so tell me if you get any errors

php: how to change string data become numeric data

can you tell how to change this result in php and mysql script:
Model Class
Ball S
Book A
Spoon
Plate B
Box C
this is my DB:
CREATE TABLE IF NOT EXISTS `inspection_report` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`Model` varchar(14) NOT NULL,
`Serial_number` varchar(8) NOT NULL,
`Lot_no` varchar(6) NOT NULL,
`Line` char(5) NOT NULL,
`Class` char(1) NOT NULL,
`Status` varchar(6) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `Model` (`Model`,`Serial_number`,`Lot_no`,`Line`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=48 ;
how if i want to show result like:
Model s a b c
Ball 1 0 0 0
Book 0 1 0 0
Spoon 0 0 0 0
Plate 0 0 1 0
Box 0 0 0 1
What's query to make this? thanks.
SELECT `Model`,
IF(`Class`='S', 1, 0) AS `S`,
IF(`Class`='A', 1, 0) AS `A`,
IF(`Class`='B', 1, 0) AS `B`,
IF(`Class`='C', 1, 0) AS `C`
FROM `inspection_report`
Your question is a little unclear, but I'm assuming you have the input data in an array mapping name to defect, and you want for each row a 1 in the appropriate column and a zero everywhere else. If so, it's just this:
$arr = array('blue' => 'S', 'red' => 'A', 'yellow' => null, 'green' => 'B', 'black' => 'C');
$defects = array_filter(array_unique(array_values($arr)));
echo "name\t";
echo implode("\t", $defects);
echo "\n";
foreach($arr as $name => $defect) {
echo "$name";
foreach($defects as $test) {
echo "\t";
echo $test == $defect ? 1 : 0;
}
echo "\n";
}
Very crude example, you'd probably go with HTML tables in reality.
<?php // $rows = array(array('name' => 'blue', 'class_defect' => 'S'), ...); ?>
<pre>
name s a b c
<?php
foreach ($rows as $row) {
printf('%-10s', $row['name']); // padding with spaces
foreach (array('s', 'a', 'b', 'c') as $col) {
echo (strtolower($row['class_defect']) == $col) ? 1 : 0;
echo ' '; // just padding again
}
}
?>
</pre>

MySQL query optimization

$res (array)-> (count 50 (!) )
Example:
(
[1] => Array
(
[artistname] => Lady GaGa
[songname] => Love Games
[duration] => 3:31
[url] => 7e91a5ca16ae
[server] => 3
)
[2] => Array
(
[artistname] => DJ Layla
[songname] => Single Lady
[duration] => 3:20
[url] => f0906a3087eb
[server] => 3
)
[3] => Array
(
[artistname] => Lady Gaga
[songname] => Bad Romance (Bimbo Jones Clean Radio Remix)
[duration] => 3:59
[url] => 36e77d5a80357
[server] => 3
)
}
PHP code:
$massquery = '';
foreach($res as $value)
{
if(!get_magic_quotes_gpc())
{
$value['artistname'] = mysql_escape_string($value['artistname']);
$value['songname'] = mysql_escape_string($value['songname']);
$value['duration'] = mysql_escape_string($value['duration']);
$value['url'] = mysql_escape_string($value['url']);
$value['server'] = mysql_escape_string($value['server']);
}
$value['artistname'] = trim($value['artistname']);
$value['songname'] = trim($value['songname']);
$value['duration'] = trim($value['duration']);
$value['url'] = trim($value['url']);
$value['server'] = trim($value['server']);
$sh = mysql_query("SELECT `artistname`,`songname`,`server` FROM `music` WHERE `artistname`='".$value['artistname']."' AMD `songname`='".$value['songname']."' AND `server`='".$value['server']."' LIMIT 1");
if(!mysql_num_rows($sh))
{
$massquery .= '("'.$value['artistname'].'", "'.$value['songname'].'", "'.$value['duration'].'", "'.$value['url'].'", "'.$value['server'].'"),';
}
}
if(!empty($massquery))
{
$massquery = substr($massquery, 0, -1);
$query = mysql_query('INSERT INTO `music` (`artistname`, `songname`, `duration`, `url`, `server`) VALUES '.$massquery);
}
mysql_close($mysql);
It turns out 50 requests "SELECT" to the database, which is very bad = (
How can I optimize this code?
From answers:
CREATE TABLE `music` (
`id` int(50) NOT NULL auto_increment,
`artistname` varchar(50) NOT NULL,
`songname` varchar(50) NOT NULL,
`duration` varchar(6) NOT NULL,
`url` varchar(255) NOT NULL,
`server` int(5) NOT NULL,
PRIMARY KEY (`id`),
KEY `artistname` (`artistname`,`songname`,`server`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
INSERT INTO `music` VALUES ('test', 'btest', 1);
...
SELECT `artistname` , `songname` , `server`
FROM `music`
WHERE FALSE
OR (
`artistname` = 'test'
AND `songname` = 'btest'
AND `server` = '1'
)
OR (
`artistname` = 'sas'
AND `songname` = 'asf'
AND `server` = '1'
)
LIMIT 0 , 30
How do I INSERT those songs that are not yet in the database?
Sorry for bad english
You want to insert new records only if no other record with the tuple (artistname,songname,server) (already) exists.
If you create a unique index for these three fields MySQL won't insert a doublet. Then you can either use something like
INSERT IGNORE INTO
tablename
(a,b,c,x,y,z)
VALUES
(1,2,3,4,5,6),
(7,8,9,10,11,12),
...
(95,96,97,98,99,100)
or a prepared statement, e.g.
$pdo = new PDO("mysql:host=localhost;dbname=test", 'localonly', 'localonly');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
/* test table */
$pdo->exec('
CREATE TEMPORARY TABLE foo (
id int auto_increment,
artistname varchar(64) not null,
songname varchar(64) not null,
duration varchar(16) not null,
url varchar(64) not null,
server int not null,
primary key(id),
unique key (artistname,songname,server)
)
');
$data = array(
array(':artistname' => 'Lady GaGa', ':songname' => 'Love Games', ':duration' => '3:31', ':url' => '7e91a5ca16ae', ':server' => 3),
array(':artistname' => 'DJ Layla', ':songname' => 'Single Lady', ':duration' => '3:20', ':url' => 'f0906a3087eb', ':server' => 3),
array(':artistname' => 'Lady Gaga', ':songname' => 'Bad Romance (Bimbo Jones Clean Radio Remix)', ':duration' => '3:59', ':url' => '36e77d5a80357', ':server' => 3)
);
/* the "actual" test script */
$stmt = $pdo->prepare('
INSERT IGNORE INTO
foo
(duration, artistname, songname, server, url)
VALUES
(:duration, :artistname, :songname, :server, :url)
');
// first run, all three records should be inserted
foreach( $data as $params ) {
$stmt->execute($params);
}
// second run
// same artist/songname, different server
$newData = $data[0]; $newData[':server'] = 4;
$data[] = $newData;
// and a completly new record
$data[] = array(':artistname' => 'xyz', ':songname' => 'The ABC song', ':duration' => '2:31', ':url' => 'whatever', ':server' => 2);
// again insert all records (including the three that have already been inserted)
foreach( $data as $params ) {
$stmt->execute($params);
}
/* fetch all records */
foreach( $pdo->query('SELECT * FROM foo', PDO::FETCH_NUM) as $row ) {
echo join(', ', $row), "\n";
}
prints
1, Lady GaGa, Love Games, 3:31, 7e91a5ca16ae, 3
2, DJ Layla, Single Lady, 3:20, f0906a3087eb, 3
3, Lady Gaga, Bad Romance (Bimbo Jones Clean Radio Remix), 3:59, 36e77d5a80357, 3
4, Lady GaGa, Love Games, 3:31, 7e91a5ca16ae, 4
5, xyz, The ABC song, 2:31, whatever, 2
The first three records have not been duplicated.
Create a single select for all the relevant cases like this, and verify the results by means of PHP:
$sh = "SELECT `artistname`,`songname`,`server` FROM `music` WHERE ";
$pq = ""
foreach($res as $value)
{
if(!get_magic_quotes_gpc())
{
$value['artistname'] = mysql_escape_string($value['artistname']);
$value['songname'] = mysql_escape_string($value['songname']);
$value['duration'] = mysql_escape_string($value['duration']);
$value['url'] = mysql_escape_string($value['url']);
$value['server'] = mysql_escape_string($value['server']);
}
$value['artistname'] = trim($value['artistname']);
$value['songname'] = trim($value['songname']);
$value['duration'] = trim($value['duration']);
$value['url'] = trim($value['url']);
$value['server'] = trim($value['server']);
$sh .= $pq . `(artistname`='".$value['artistname']."' AMD `songname`='".$value['songname']."' AND `server`='".$value['server']."')");
$pq = " OR ";
}
$res = mysql_query($sh);
The select query you wrote can't be inside the foreach, or of course it'll query each time. Turn your $res into a long WHERE clause:
$sql = "SELECT `artistname`,`songname`,`server` FROM `music` WHERE FALSE ";
foreach($res as $value)
{
// ...
$sql .= "OR (`artistname`='".$value['artistname']."' AND `songname`='".$value['songname']."' AND `server`='".$value['server'].")";
}
and then run that query against the database and build your INSERT query.

Categories