Nette - database INSERT - number of affected rows - nette

I would like to find out the number of affected (inserted) rows after inserting into the table. I didn't figure out how to do it in the documentation. The update returns the number of affected rows. The insert returns Nette\Database\Table\ActiveRow
How do I get it?
$affected = $context->table('author')->insert([
[
'name' => 'Sansa Stark',
'born' => null
], [
'name' => 'Arya Stark',
'born' => null
]
]);
bdump($affected); // Nette\Database\Table\ActiveRow - I need the number of inserted records

Nette Database Explorer doesn't return count after insert(). It is not useful information as long as you can count data before insert by yourself.
$data = [...];
$count = count($data);
$context->table('author')->insert($data);
It works only with update and delete as is mentioned in documentation.
$count = $context->table('author')
->where('id', 10)
->delete();
It might be possible with getRowCount() over query in Nette Database Core

Nette Database Core is built upon PDO. Alas, the authors tend to create their own objects instead of extending PDO, which makes such elementary operations tedious:
// get Nette ResultSet object
$resultSet = $this->database->query("INSERT/UPDATE/DELETE...");
// get original PDOStatement object
$pdoStatement = $resultSet->getPdoStatement();
// get the affected rows from PDO object (instead of $resultSet->rowCount())
$pdoStatement->rowCount();
A word of warning for those considering Nette for production: There is no real documentation, only cook books and autogenerated PHPDoc which just prints names without any explanation.

Related

what is %d used for in arrays on wordpress?

I presume it means double but what does it mean in general?
$wpdb->delete(
"data",
[ 'id' => $id ],
[ '%d' ]
);
I get that I'm deleting from table data using array id => id but what is the %d for?
Why am I asking? well, I'd like to do some bulk updates as shown through the features built in with wp_list_table as shown through this tutorial. As I've looked around for that first link I found a few more sources on how to fix my table - I'm just trying do multiple operations and I'm stuck with bulkupdater
its to identify the type of data to be deleted think where=%d which means interger
others include:
%d - interger (just to make it clear)
%f - float
%s - string
so your code
$wpdb->delete(
"data",
[ 'id' => $id ],
[ '%d' ]
);
Is only going to delete an id where the id string is an integer e.g. 43 not if a43
You can have obviously a few types in an array, but they need to match the order of the data array (i.e. array('id'=>$id, 'numval'=>$num) ,array(integer, integer) )

Drupal 7 | update multiple rows using db_update

I have an array like
Array (
[1] => 85590762,22412382,97998072
[3] => 22412382
)
Where key is the item_id and value is the value of a column which I need to update against an item. I can use db_update in a loop but i want to avoid this strategy due to performance. I want to update all the rows in a single db call. Also using db_query I think will not be a good idea. So is there any way using db_update to update these rows?
According to above data, standard mysql queries will be like
update items set sold= 1, users = '85590762,22412382,97998072' Where item_id = 1;
update items set sold = 1, users = '22412382' Where item_id = 3;
Unfortunately you can't do that. Currently and probably in the feature, there won't be support for updating (multiple) values on different rows with different values with one db_update.
If your data is this:
$for_update = array(
1 => "85590762,22412382,97998072",
3 => "22412382",
);
You can do these:
CASE 1: Create a loop and call every time the update function:
foreach ($for_update as $key => $value) {
$fields = array('sold' => 1, 'users' => $value);
db_update('items')->fields($fields)->condition('item_id', $key, '=')->execute();
}
Pros: other modules can hook inside your query, can support multiple DB drivers
Cons: Object initialization is slow, lots of DB connection/traffic
CASE 2: It's faster if you use db_query()...
... because in that case there's no object instantiation/operation, which is a bit costly and other conversion. (i.e.: https://drupal.stackexchange.com/questions/129669/whats-faster-db-query-db-select-or-entityfieldquery)
foreach ($for_update as $key => $value) {
db_query("UPDATE items SET sold = :sold, users = :users WHERE item_id = :item_id",
array(':sold' => 1, ':users' => $value, ':item_id' => $key)
);
}
Pros: no object initialization and operation < so it's faster
Cons: other modules can't hook inside your query, your code can break under different DB drivers, lots of DB connection/traffic
CASE 3: Also you can use transaction
This can boost a bit your performance at some case.
$transaction = db_transaction();
try {
foreach ($for_update as $key => $value) {
$fields = array('sold' => 1, 'users' => $value);
db_update('items')->fields($fields)->condition('item_id', $key, '=')->execute();
}
}
catch (Exception $e) {
$transaction->rollback();
watchdog_exception('my_type', $e);
}
Note: transaction mostly used, when you want everything or nothing. But with some DB drivers, you can optimize out the COMMIT commands.
In case 1 you get something like this:
UPDATE items SET sold = 1, users = '85590762,22412382,97998072' WHERE item_id = 1;
COMMIT;
UPDATE items SET sold = 1, users = '22412382' WHERE item_id = 3;
COMMIT;
With transaction you do something like this:
UPDATE items SET sold = 1, users = '85590762,22412382,97998072' WHERE item_id = 1;
UPDATE items SET sold = 1, users = '22412382' WHERE item_id = 3;
COMMIT;
With COMMIT you write out the data to the final place (into long-term storage, until this, it's only somewhere in the memory or a temporary place). When you use rollback, you drop these changes. At least this is how I understand this. (i.e. I get performance improvement with this when I used sqlite.)
Same as case 1 or 2 +
Pros: you can rollback your data if you need to be consistent, you write out data only once to the drive < so sql only "once" do IO, lots of DB connection/traffic
CASE 4: Or you can build one sql statement for this:
$ids = array();
$when_then = "";
foreach ($for_update as $key => $value) {
$ids[] = $key;
$when_then .= "WHEN $key THEN '$value' ";
}
db_query("UPDATE items
SET sold = 1, users = CASE item_id
$when_then
ELSE users
END
WHERE item_id IN(:item_ids)", array(':item_ids' => $ids));
Probably this is the fastest way from all from here.
NOTE: $when_then variable doesn't contain the best solution, it's better if you can use the params or you escape unsafe data.
Pros: between your server and sql there will be only one request and less redundant data, one bigger sql queries faster then a lot of small ones
Cons: other modules can't hook inside your query, your code can break under different DB drivers
Also please note, after (I think) PHP 5.3 you can't run more than one statement in db_query or in PDO by default, because of it can be an SQL Injection, so it blocks. However you can set this, but not recommended. (PDO support for multiple queries (PDO_MYSQL, PDO_MYSQLND))
13-05-2019 edit:
Just a side note, I did a few month ago some performance measurement on medium dataset to query down some data via db_select and db_query. The magnitude of the performance of these are: under you run one db_select, you can run two db_query. This also applies to db_update and db_query. The test queried down some columns with a simple SELECT, no JOINS, no fancy dandy stuff, just two condition. Of course, the measurement also contained/included the time which was required to build up these queries (at both case). However please note, the Drupal Coding Standard requires to use db_update, db_delete, db_merge on modification normally, only 'allows' db_query instead of db_select operation.
Using BULK UPDATE you can update multiple values on diferent rows or in the same row with the dinamic query db_update .
Note: By bulk updating only one query can be sent to the server instead of one query for each row to update.
Syntax:
UPDATE yourTableName
SET yourUpdateColumnName =
(CASE yourConditionColumnName WHEN Value1 THEN 'UpdatedValue'
WHEN Value2 THEN 'UpdatedValue'
.
.
N
END)
WHERE yourConditionColumnName IN(Value1,Value2,.....N);
More info about expressions on UpdateQuery on:
https://api.drupal.org/api/drupal/includes%21database%21query.inc/function/UpdateQuery%3A%3Aexpression/7.x
With your data will be:
//Create array
$for_update = [
1 => "85590762,22412382,97998072",
3 => "22412382",
];
$item_ids_to_filter = [];
//Setup when-then conditions
$when_then = "CASE item_id ";
foreach ($for_update as $item_id => $users_id) {
$when_then .= "WHEN {$item_id} THEN '{$users_id}' ";
$item_ids_to_filter[] = $item_id;
}
$when_then .= "END";
//Execute query and update the data with db_update
db_update('items')
->expression("users",$when_then)
->condition('item_id', $item_ids_to_filter)
->execute();
As result the next query is generated:
UPDATE items
SET users =
(CASE item_id = WHEN 1 THEN '85590762,22412382,97998072'
WHEN 3 THEN '22412382'
END)
WHERE (item_id IN ('1', '3'))
I think you could do something like
$query = db_udpate('table')->fields(array('field_1','field_2','field_3'));
foreach ($fields as $record) {
$query->values($record);
}
return $query->execute();
Where $fields = array ('field_1' => VALUE, 'field_2' => VALUE)

PODS CMS incorrect query

I have a query like this:
$Record = new Pod('event');
$where_clause = "DATE(enddate) >= CURDATE() AND event_type.name='Training'";
$Record->find('startdate ASC', 100, $where_clause);
It return only 1 item while I have 2 satisfy the query.
I have check with each query:
$Record = new Pod('event');
$where_clause = "DATE(enddate) >= CURDATE()";// AND
$Record->find('startdate ASC', 100, $where_clause);
And
$Record = new Pod('event');
$where_clause = "event_type.name='Training'";// AND
$Record->find('startdate ASC', 100, $where_clause);
I got 2 items on both queries.
Can you give some hints to fix this? Thanks in advance.
The code you are using is designed to work with Pods 1.X, but your query itself looks good. With Pods 2.X you would want to do the code below, which combines the pods() global function with its method find and then uses the total method to check the number of items returned.
$param = array(
"where" => "DATE(enddate) >= CURDATE() AND event_type.name='Training'",
"orbery" => "t.startdate ASC",
"limit" => "100",
);
$pod = pods( 'event', $param );
$number = $pod->total();
You know have the records in $pod and the total number in $number, which you can use to control the loop when you loop through the results in $pod. If you wanted to you could use total_found to get the number of records that would have been returned had you not limited it.
Also, have you checked that you really have two records that should be returned. It is possible that when you combine the two queries only one of them meets both conditions.
You should also check out Perry Bonwell's tutorial where he explains how to query for future events. He uses PHP, not SQL functions to handle the dates, but otherwise he has a similar approach.

How can I order by NULL in DQL?

I'm building an app using Symfony2 framework and using Doctrine ORM. I have a table with airlines for which some IATA codes are missing. I'm outputting a list, ordered by this IATA code, but I'm getting the undesirable result that the records with null IATA codes are sorted at the top.
In MySQL this is simple enough to do, with ORDER BY ISNULL(code_iata), code_iata but I'm clueless as to what the equivalent would be for DQL. I tried
$er->createQueryBuilder('airline')->orderBy('ISNULL(airline.codeIata), airline.codeIata', 'ASC')
but this gives me a syntax error.
The Doctrine docs give me no answer either. Is there a way?
You can use the following trick in DQL to order NULL values last
$em->createQuery("SELECT c, -c.weight AS HIDDEN inverseWeight FROM Entity\Car c ORDER BY inverseWeight DESC");
The HIDDEN keyword (available since Doctrine 2.2) will result in omitting the inverseWeight field from the result set and thus preventing undesirable mixed results.
(The sort fields value is inverted therefore the order has to be inverted too, that's why the query uses DESC order, not ASC.)
Credits belong to this answer.
The most unobtrusive generic solution would be to use the CASE expression in combination with the HIDDEN keyword.
SELECT e,
CASE WHEN e.field IS NULL THEN 1 ELSE 0 END HIDDEN _isFieldNull
FROM FooBundle:Entity e
ORDER BY _isFieldNull ASC
Works with both numeric as well as other field types and doesn't require extending Doctrine.
If you want to do something similar to "NULLS LAST" in SQL (with PostgreSQL in my case):
ORDER BY freq DESC NULLS LAST
You can use the COALESCE function with the Doctrine Query Builder
(HIDDEN will hide the field "freq" on your query result set).
$qb = $this->createQueryBuilder('d')
->addSelect('COALESCE(d.freq, 0) AS HIDDEN freq')
->orderBy('freq', 'DESC')
->setMaxResults(20);
Here it is an example for a custom walker to get exactly what you want. I have taken it from Doctrine in its github issues:
https://github.com/doctrine/doctrine2/pull/100
But the code as it is there didn't work for me in MySQL. I have modified it to work in MySQL, but I haven't test at all for other engines.
Put following walker class for example in YourNS\Doctrine\Waler\ directory;
<?php
namespace YourNS\Doctrine\Walker;
use Doctrine\ORM\Query\SqlWalker;
class SortableNullsWalker extends SqlWalker
{
const NULLS_FIRST = 'NULLS FIRST';
const NULLS_LAST = 'NULLS LAST';
public function walkOrderByClause($orderByClause)
{
$sql = parent::walkOrderByClause($orderByClause);
if ($nullFields = $this->getQuery()->getHint('SortableNullsWalker.fields'))
{
if (is_array($nullFields))
{
$platform = $this->getConnection()->getDatabasePlatform()->getName();
switch ($platform)
{
case 'mysql':
// for mysql the nulls last is represented with - before the field name
foreach ($nullFields as $field => $sorting)
{
/**
* NULLs are considered lower than any non-NULL value,
* except if a – (minus) character is added before
* the column name and ASC is changed to DESC, or DESC to ASC;
* this minus-before-column-name feature seems undocumented.
*/
if ('NULLS LAST' === $sorting)
{
$sql = preg_replace_callback('/ORDER BY (.+)'.'('.$field.') (ASC|DESC)/i', function($matches) {
if ($matches[3] === 'ASC') {
$order = 'DESC';
} elseif ($matches[3] === 'DESC') {
$order = 'ASC';
}
return ('ORDER BY -'.$matches[1].$matches[2].' '.$order);
}, $sql);
}
}
break;
case 'oracle':
case 'postgresql':
foreach ($nullFields as $field => $sorting)
{
$sql = preg_replace('/(\.' . $field . ') (ASC|DESC)?\s*/i', "$1 $2 " . $sorting, $sql);
}
break;
default:
// I don't know for other supported platforms.
break;
}
}
}
return $sql;
}
}
Then:
use YourNS\Doctrine\Walker\SortableNullsWalker;
use Doctrine\ORM\Query;
[...]
$qb = $em->getRepository('YourNS:YourEntity')->createQueryBuilder('e');
$qb
->orderBy('e.orderField')
;
$entities = $qb->getQuery()
->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, '\YourNS\Doctrine\Walker\SortableNullsWalker')
->setHint('SortableNullsWalker.fields', array(
'sortOrder' => SortableNullsWalker::NULLS_LAST
))
->getResult();
DQL does not contain every function of plain SQL. Fortunately you can define your custom DQL method to accomplish this.
Some resources:
http://punkave.com/window/2012/07/24/for-the-php-crowd-adding-custom-functions-to-doctrine-2-dql
http://docs.doctrine-project.org/en/2.1/cookbook/dql-user-defined-functions.html
http://symfony.com/doc/2.0/cookbook/doctrine/custom_dql_functions.html
By default, MySQL will still sort a NULL value; it will just place it at the beginning of the result set if it was sorted ASC, and at the end if it was sorted DESC. Here, you're looking to sort ASC, but you want the NULL values to be at the bottom.
Unfortunately, as powerful as it is, Doctrine isn't going to offer much support here, since function support is limited, and most of it is limited to SELECT, WHERE, and HAVING clauses. You actually wouldn't have a problem at all if any of the following were true about the QueryBuilder:
select() accepted ISNULL()
orderBy() or addOrderBy() supported ISNULL()
the class supported the concept of UNIONs (with this, you could run two queries: one where the codeIata was NULL, and one where it wasn't, and you could sort each independently)
So that said, you can go with the user-defined functions that ArtWorkAD mentioned already, or you could replicate that last point with two different Doctrine queries:
$airlinesWithCode = $er->createQueryBuilder("airline")
->where("airline.iataCode IS NULL")
->getQuery()
->getResult();
$airlinesWithoutCode = $er->createQueryBuilder("airline")
->where("airline.iataCode IS NOT NULL")
->getQuery()
->getResult();
Then you can combine these into a single array, or treat them independently in your templates.
Another idea is to have DQL return everything in one data set, and let PHP do the heavy lifting. Something like:
$airlines = $er->findAll();
$sortedAirlines = array();
// Add non-NULL values to the end if the sorted array
foreach ($airlines as $airline)
if ($airline->getCodeIata())
$sortedAirlines[] = $airline;
// Add NULL values to the end of the sorted array
foreach ($airlines as $airline)
if (!$airline->getCodeIata())
$sortedAirlines[] = $airline;
The downside to both of these is that you won't be able to do LIMITs in MySQL, so it might only work well for relatively small data sets.
Anyway, hope this gets you on your way!

Possible to convert result of Drupal db_query to PHP array?

In Drupal, I can execute a SQL as follows:
$query_object = db_query("SELECT * FROM {nodes}");
If I know the query returns only a single result (so only 1 row and 1 column), I can directly fetch it with:
$result = db_result($query_object);
If I got multiple results, I need to loop through them with something like:
$rows[] = array();
while (($row = db_fetch_object($query_object) != FALSE) {
$rows[] = $row;
}
I'm wondering if there is an easier way to do that? Is there a way that I can transfer all results into an array with a single statement? Or isn't that working, because db_result returns a cursor-like object, where you can only fetch a single row each time?
Not in Drupal 6.
In Drupal 7, there are fetch methods that can help to avoid loops like that. From http://drupal.org/node/310072:
<?php
// Retrieve all records into an indexed array of stdClass objects.
$result->fetchAll();
// Retrieve all records into an associative array keyed by the field in the result specified.
$result->fetchAllAssoc($field);
// Retrieve a 2-column result set as an associative array of field 1 => field 2.
$result->fetchAllKeyed();
// You can also specify which two fields to use by specifying the column numbers for each field
$result->fetchAllKeyed(0,2); // would be field 0 => field 2
$result->fetchAllKeyed(1,0); // would be field 1 => field 0
// Retrieve a 1-column result set as one single array.
$result->fetchCol();
// Column number can be specified otherwise defaults to first column
$result->fetchCol($column_index);
?>
In Drupal 7, you can also use:
db_query('QUERY')->fetchAll(PDO::FETCH_ASSOC);
I do always something like this ( just a simple exemple) :
$query = db_query("SELECT nid
FROM {from}
WHERE blallala
",
$tab_arg
);
if ($query->rowCount() == 0) {
$output=t('no result')
} else
{
foreach($query as $result)
{
$tab_res[]=$result;
}
foreach($tab_res as $res)
{
$output.=$res->nid;
}
}
One can also use db_fetch_array($result), where $result =db_query($queryString). As explained from the Drupal documentation:
[It returns] ...an associative array representing the next row of the result, or
FALSE. The keys of this object are the names of the table fields
selected by the query, and the values are the field values for this
result row.

Resources