php: how to change string data become numeric data - php

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>

Related

PHP MySQL Duplicate entry exception - InsertMulti with OnDuplicate

I am using ThingEngineer/PHP-MySQLi-Database-Class and I am trying to perform a multiple insert while using OnDuplicate. The goal is to insert a new product record if the 'sku' does not already exist. If the 'sku' does exist then the 'name' should be updated instead of creating a new entry.
MySQL Schema:
CREATE TABLE `products` (
`product_pk` bigint(9) NOT NULL,
`product_id` int(20) UNSIGNED NOT NULL,
`name` varchar(255) NOT NULL,
`sku` varchar(16) NOT NULL,
`category` int(10) DEFAULT NULL,
`last_update` timestamp NOT NULL ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
ALTER TABLE `products`
ADD PRIMARY KEY (`product_pk`),
ADD UNIQUE KEY `sku` (`sku`);
ALTER TABLE `products`
MODIFY `product_pk` bigint(9) NOT NULL AUTO_INCREMENT;
PHP:
$sDate = date("Y-m-d H:i:s");
$lastid = $db->rawQuery('SELECT MAX( product_id ) AS max FROM products');
(!$lastid || !isset($lastid[0]['max'])) ? $pid = 0 : $pid = $lastid[0]['max']++;
foreach ($data as $item){
if (isset($item['sku']) && !null == $item['sku']){
$prod[$pid]['product_id'] = $pid;
$prod[$pid]['sku'] = $item['sku'];
$prod[$pid]['name'] = substr($item['product-name'],0,255);
$prod[$pid]['last_update'] = $sDate;
$pid++;
}
}
$utfEncodedArray =encodeArray($prod, 'UTF-8');
$db->onDuplicate('name', 'product_pk');
$db->insertMulti('products', $utfEncodedArray);
function encodeArray($array, $type)
{
foreach($array as $key => $value)
{
if (is_array($value)){ $array[$key] = encodeArray($value, $type);}else{ $array[$key] = mb_convert_encoding($value, $type);}
}
return $array;
}
The error I receive is:
Uncaught mysqli_sql_exception: Duplicate entry 'ABC123' for key 'sku'
Here is a sample of the array $utfEncodedArray used on the insertMulti call:
Array(
[1] => Array
(
[product_id] => 1
[sku] => ABC123
[name] => product1
[last_update] => 2018-09-08 18:55:20
)
[2] => Array
(
[product_id] => 2
[sku] => ABC124
[name] => product2
[last_update] => 2018-09-08 18:55:20
)
)
Steps I have tried so far:
Dropped the 'products' table and created it again. Multiple times.
Tried using 'sku' instead of 'product_pk' in the onDuplicate call.
Tried multiple collation types
Tried using unique key on both 'sku' and 'product_id'
When I attempted this method all entries were inserted correctly but when running it again it generated duplicates instead of updating the existing row. Not sure how this happened seeing as both 'sku' and 'product_id' are unique.
The $prod array currently contains the same values. So every time I run this I would expect to see the 'last_updated' column to be updated every time after the initial inserts.
This is my first experience using onDuplicate and despite hours of searching and reading docs I am still lost. I was trying to let the db class handle the multiple insert from the array but I am not against trying raw queries while iterating over my array of products instead.
Of course as soon as I posted this I find the issue...
Found a fork of the database class which resolved issues with insertMulti while using onDuplicate:
should fix insertMulti() if onDuplicate() is set

Weird error when using MySQL when using PDO

I encountered a very strange bug today. I am getting sideblinded by this so bad as it's breaking my entire application.
So, I have this little framework that I've built where I have a standard modell, so snippeting this will be a little long and descriptive.
<?php include('inc/inc.php'); ?>
<?php
if(!empty($_POST['answer']) && !empty($_POST['levelstart'])){
if($stmt = $site->answerQuestion($_POST['levelstart'], $_POST['answer'])){
if($stmt[0]){
echo json_encode(array('success' => true, 'correct' => $stmt[1], 'correctanswer' => $stmt[2], 'round_end' => $stmt[3]));
}else{
echo json_encode(array('success' => false, 'error' => 'error occurred'.$stmt[1]));
}
}else{
echo json_encode(array('sucess' => false, 'error' => 'Unknown error'));
}
}else{
echo json_encode(array('success' => false, 'error' => 'Provide all necessary parameters.'));
}
?>
this piece of code outputs the following.
INSERT INTO quiz_level_starts (`user_id`, `question_id`, `time`, `round_id`, `type`, `success`, `ref_id`) VALUES ('4', '10', '1471887809', '', '1', '1', '905'){"success":false,"error":"error occurred23000"}
The generated query above is only a dummy one that i simple put together so I don't need the parameterization for simply testing. The "error" key in the json array contains error data, and the errorcode is dumped there.
23000 is the mysql error code for there being a duplicate unique column, but there is no unique column that I'm using in the query(see table struct below.)
Since the function answerQuestion is a very long one, I'll only paste the related lines next. In $site->answerQuestion it calls a function called "insertLevelStarts" which is supposed to insert an entry to the db.
This is how i call it:
if($stmtss = $this->db->insertLevelStarts($_SESSION['user']['id'], $stmts['return'][0]['id'], time(), $roundid, 1, 1, $levelstart)){
And this is how it's declared, also the rest of the related and unknown code:
public function insertLevelStarts($user_id, $question_id, $time, $round_id, $type = 0, $success = 0, $refid = 0){
/*
Type=0 start 1 stop
success=0 for start 1 if successfull on stop
*/
$query = 'INSERT INTO quiz_level_starts (`user_id`, `question_id`, `time`, `round_id`, `type`, `success`, `ref_id`) VALUES (:user_id, :question_id, :time, :round_id, :type, :success, :refid)';
echo $this->genFakeQuery($query, array(
':user_id' => $user_id,
':question_id' => $question_id,
':time' => $time,
':type' => $type,
':success' => $success,
':refid' => $refid,
':round_id' => $round_id
));
return $this->execInsert($query, array(
':user_id' => $user_id,
':question_id' => $question_id,
':time' => $time,
':type' => $type,
':success' => $success,
':refid' => $refid,
':round_id' => $round_id
)
);
}
public function genFakeQuery($query, $array){
foreach($array as $key => $val){
$query = str_replace($key, "'$val'", $query);
}
return $query;
}
public function execUpdate($query, $preparray, $updatearr){
try {
$stmt = $this->db->prepare($query);
$stmt->execute(array_merge($preparray, $updatearr));
$rows = $stmt->rowCount();
if($rows > 0){
return array('type' => 'rowsaffected', 'return' => $rows);
}else{
return array('type' => 'noreturn', 'return' => 'none');
}
} catch(PDOException $ex) {
return array('type' => 'error', 'return' => $ex);
}
}
public function updateClause($query, $update, $updatearr){
if(count($update) > 0){
$count = 0;
foreach($update as $k => $v){
if($count > 0){
$query .= ',';
}
$query .= " `$k` = :$k";
$updatearr[":$k"] = $v;
$count++;
}
}
return array('query' => $query, 'updatearr' => $updatearr);
}
The aforementioned query
INSERT INTO quiz_level_starts (`user_id`, `question_id`, `time`, `round_id`, `type`, `success`, `ref_id`) VALUES ('4', '10', '1471887809', '', '1', '1', '905')
inserts into a table looking like this:
CREATE TABLE IF NOT EXISTS `quiz_level_starts` (
`id` int(11) NOT NULL,
`user_id` int(11) NOT NULL,
`question_id` int(11) NOT NULL,
`time` int(11) NOT NULL,
`type` int(11) NOT NULL,
`success` int(11) NOT NULL,
`ref_id` int(11) NOT NULL,
`round_id` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
ALTER TABLE `quiz_level_starts`
ADD PRIMARY KEY (`id`);
ALTER TABLE `quiz_level_starts`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
Will greatly appriciate any help recieved.
I assume that the error occurs because round_id is an integer field that can not be NULL and has no default value and you pass it an empty value.
Try if this query works:
INSERT INTO quiz_level_starts (`user_id`, `question_id`, `time`, `round_id`, `type`, `success`, `ref_id`) VALUES ('4', '10', '1471887809', '0', '1', '1', '905')

MySQL find multiple records by two columns

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();

Build array recursively in CodeIgniter

In CodeIgniter I'm trying to create a function. Need create array who will delete rows in db like:
$selected_items_by_id = array('1','2','3','4',); // --<<<Need Create This
$this->db->where_in('id', $selected_items_by_id);
$this->db->delete('mytable');
mytabe DB Structure of:
ID | NAME | PARENT_ID
1 Item1 0 // First Root item
2 Item2 1 // First Root sub item
3 Item3 2 // First Root sub sub item
4 Item4 3 // First Root sub sub sub item
5 Item5 0 // Second Root item
Items layout:
Item1
+Item2
++Item3
+++Item4
Item5
Here I'm getting the needed item id (from select box):
$id = $this->input->post('delete_menu_item');
Logic:
If item parent_id == 0, then item is root.
If Item is root item, in array will be only root item id
In array need $id and all $id subs (if they exist)
UPDATE
I try to make recursive function outside from CI.
Search function - to separate the necessary subaray:
function search($array, $key, $value)
{
$results = array();
if (is_array($array))
{
if (isset($array[$key]) && $array[$key] == $value)
$results[] = $array;
foreach ($array as $subarray)
$results = array_merge($results, search($subarray, $key, $value));
}
return $results;
}
I'm using this array, based on DB entries:
$array = array(
array('id' => '1', 'name' => 'Item1', 'parent_id' => '0'),
array('id' => '2', 'name' => 'Item2', 'parent_id' => '1'),
array('id' => '3', 'name' => 'Item3', 'parent_id' => '2'),
array('id' => '4', 'name' => 'Item4', 'parent_id' => '3'),
array('id' => '5', 'name' => 'Item5', 'parent_id' => '0'),
);
Recursive function:
function build_array($array,$id, $final = NULL){
$data = search($array, 'id', $id);
foreach ($data as $item):
if ($item['parent_id'] == 0){
$final[] = $item['id'];
}
else {
$parent_id = $item['parent_id'];
$final[] = $item['id'];
$final[] = $parent_id;
build_array($array, $parent_id, $final);
// Here go recursive
}
endforeach;
return $final;
}
$result = build_array($array,1);
var_dump($result);
What should be the function recursively?
If I understand what you are asking, you'll need to handle each item iteratively (in a loop).
So first you get an item, then you see if it has a parent - if so, get it's parent and start again, etc. This is one of the few cases where recursive functions can really come in handy.
You just build your final arrays as you go, until you run out of items - being aware that if you aren't careful you will get an infinite loop as you pull the same items over and over again.
Without more information on what you have to input and what you want to get as output, this is about as helpful as anyone can be. For more help you'll need to clarify what you want as output, code you've tried so far and what it's doing that you don't want, etc.
Ok. I will use the sample array you put together, only slightly different: Since ids are unique, there's no reason why they shouldn't serve as keys. Like so:
$array = array(
'1'=>array('name' => 'Item1', 'parent_id' => '0'),
'2'=>array('name' => 'Item2', 'parent_id' => '1'),
'3'=>array('name' => 'Item3', 'parent_id' => '2'),
'4'=>array('name' => 'Item4', 'parent_id' => '3'),
'5'=>array('name' => 'Item5', 'parent_id' => '0'),
);
Add the following methods to your controller/model/lib/whatever:
function children_of($arr,$id)
{
$r=array();
foreach($arr as $key=>$entry)
if($entry['parent_id']==$id) $r[]=$key;
return $r;
}
function subtree_array($arr,$id)
{
if(isset($arr[$id]))
{
$r=array($id);
foreach($this->children_of($arr,$id) as $child)
$r=array_merge($r,$this->subtree_array($arr,$child));
return $r;
}
else
return array(0); // (0) comes handy in SQL IN operations
}
children_of() returns the immediate children of $id in $arr.
subtree_array() returns an array including $id and all of its descendants in $arr. If $id is not a key of $arr, it returns array(0). That's because you say you want to use it in an SQL query, and a where clause like where xyz in () would be bogus, while where xyz in (0) wouldn't, and also would always return false since no item has a zero id (it seems to be reserved to denote root nodes).
So, the usage might be something like:
$id=$this->input->post('delete_menu_item');
$selected_items_by_id=subtree_array($aray,$id);
I tried to approach the task from the other side.
Using cascade delete, and everything is going sql side:
CREATE TABLE `navigation` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(50) NOT NULL COLLATE 'utf8_general_ci',
`url` VARCHAR(50) NOT NULL COLLATE 'utf8_unicode_ci',
`position` MEDIUMINT(8) NOT NULL DEFAULT '100',
`parent_id` INT(11) NOT NULL,
PRIMARY KEY (`id`),
INDEX `parent_id` (`parent_id`),
CONSTRAINT `FK1` FOREIGN KEY (`parent_id`) REFERENCES `navigation` (`id`) ON UPDATE CASCADE ON DELETE CASCADE
)
COLLATE='utf8_unicode_ci'
ENGINE=InnoDB;
Thank you for your suggestions.

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