Is there a better way deleting dataobject older than x-days instead of using custom sql queries?
that's what I do now
$host = 'localhost';
$username = 'db123';
$password = 'pass';
$db_name = 'db123';
mysql_connect("$host", "$username", "$password")or die("cannot connect");
mysql_select_db("$db_name")or die("cannot select DB");
$sql = "DELETE FROM Cart WHERE Created < (CURDATE() - INTERVAL 1 DAY)";
$result = mysql_query($sql);
mysql_close();
first of all, using mysql_query is bad, not just in this case.
mysql_query has been deprecated and should no longer be used.
secondly, SilverStripe already has a DB connect, do not attempt to create another one.
SilverStripe offers many ways to query the database.
Option 1:
the cleanest way of course is to use the SilverStripe ORM.
A DataObject has a method called ->delete().
Using delete has several advantages over writing your own SQL query.
its easy
it will delete the DataObject from all its tables for you (eg the DataObject class Folder is a subclass of File, so if you do $myFolder->delete() it will go and delete this Folder in the File Table but also in the Folder Table (if there is a Folder table))
it will execute the SilverStripe delete hooks (onBeforeDelete and onAfterDelete)
its pretty straight forward:
// to delete all `DataObject`s in a List, just loop it and call ->delete()
foreach($list as $item) {
$item->delete();
}
// there also is a method that does that for you, however, I would advise against it
// because it is currently inconsistently implemented and might lead to unexpected results
// $list->removeAll(); // i STRONGLY RECOMMEND AGAINST THIS
// if $list is a DataList, it will delete all records
// if $list is a ManyManyList, it will unlink all records
// if $list is a ArrayList, it will error because there is no removeAll() method on ArrayList
to solve your problem:
$date = date('Y-m-d H:i:s', strtotime("now -1 day"));
$list = Cart::get()->filter('Created:LessThan', $date);
foreach($list as $item) {
$item->delete();
}
it however has also a major disadvantage when deleting many DataObjects: performance.
I will however still recommend using ->delete() if you can say that performance is not a huge problem. The benefits usually outweigh the drawbacks.
Read more about DataList, ::get(), ->filter() and ->delete() in the SilverStripe docs for Datamodel / ORM and DataObject
Option 2:
if you however really need to, it is possible to use a lower level of the ORM to perform the delete:
$query = new SQLQuery();
$query->setDelete(true);
$query->setFrom('Cart');
$query->setWhere('Created < (CURDATE() - INTERVAL 1 DAY)"');
// if you want to debug the query, you can use ->sql() to get it as string
// echo $query->sql();
$query->execute();
Read more about SQLQuery in the SilverStripe docs
Option 3:
if you for some reason really want to avoid the ORM at all, SilverStripe also lets you execute raw SQL queries:
DB::query("DELETE FROM Cart WHERE Created < (CURDATE() - INTERVAL 1 DAY)");
Related
I am creating articles in Joomla! programmatically using JTable.
Since I have a lot of articles that need to get synchronized periodically, I need to check each article if it already exists before inserting it (otherwise it produces errors).
What is the best way to do so?
My idea was to retrieve all articles from database and compare unique fields. But problems (blank page) occured while retreiving the articles. Here is the code:
function getExistingArticles(){
// Create a new query object.
$db = JFactory::getDbo();
$query = $db->getQuery(true);
$query->select('*')->where('`a.created_by_alias`= `article_synchronizer`'); // Prepare query.
$query->from('`#___categories` AS a');
// Reset the query using newly populated query object.
$db->setQuery($query);
$articles = $db->loadObjectList(); // Execute query, return result list.
return $articles;
}
If this is the "best" way in Joomla! to check if a certain article already exists, where is the problem in this code, that results in a blank page?
Otherwise which is the best way to check if a Joomla! article with a certain content already exists?
I haven't tested your query, but I would suggest quoting column names and values using Joomla's API like so:
function getExistingArticles()
{
$db = JFactory::getDbo();
$query = $db->getQuery(true);
$query->select($db->quoteName(array('a.*')))
->from($db->quoteName('#__categories', 'a'))
->where($db->quoteName('a.created_by_alias') . ' = ' . $db->quote('article_synchronizer')); // Quoted query.
$db->setQuery($query);
$articles = $db->loadObjectList();
return $articles;
}
I've fixed your query a little. You were using 3 underscores when defining the table prefix (only 2 should be used). The where and from clauses were also the wrong way round.
I would like to retrieve all the tables of my database as a list.
i tried to do a "Show databases" on a query but as i'm not using a class I defined (entity) in symfony it's not working.
And with DQL :
$em = $this->getDoctrine()->getEntityManager();
$query = $em->createQuery(
'show databases');
$result = $query->getResult();
This error :
[Syntax Error] line 0, col 0: Error: Expected SELECT, UPDATE or DELETE, got 'show'
Any idea to help me ?
As mentioned in a different answer, you can use Doctrine\DBAL for that:
/** #type \Doctrine\DBAL\Connection $connection */
$connection = ...;
/** #type \Doctrine\DBAL\Schema\MySqlSchemaManager $sm */
$sm = $connection->getSchemaManager();
And then just list the tables as Array:
var_dump( $sm->listDatabases() );
My 2 cents:
getContainer()->get('doctrine.dbal.default_connection')->getSchemaManager()->listTableNames()
This will give you an array of table names.
i have a couple cases where I need to use complex sql statements/functions that I just couldn't do in DQL. Luckily, Symfony2/doctrine provide a method to grab the current database connection and bypass doctrine entirely.
//get connection
$conn = $this->get('database_connection');
//run a query
$users= $conn->fetchAll('select * from users');
Be very careful when using this method, however. Since you are bypassing doctrine, you need to handle any security concerns like SQL injection yourself.
You can add Doctrine DBAL to your project and it will give you the tools you need. You can list databases, tables from a database, columns from a table etc etc
More in Doctrine DBAL documentation: http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/schema-manager.html
Doctrine is a ORM, it is not intended to list all the databases. Beside that, usually for the current user you don't have the right to show all databases in the server, this can prove to be a big security breach.
Basicly, doctrine does not know how to interpret your query, you have to use a native query for this: Doctrine Native Query
I am working on one company internal web software and in this I have given forum option for internal employee.Now I have to add second company to forum so that second company employee can also give some comment in forum.I have two database one for my company internal and second is second company database.
Please Help me how can I Implement this???
Always use resource returned by mysql_connect() so you know what database you use
// database 1
$db1 = mysql_connect();
// database 2
$db2 = mysql_connect();
// query db1
mysql_query($query,$db1);
Also try reading the docs about it
It depends on what database you are using..,,
and try to read some mysql function cause theres a function for linking databases..
You should use 2 (3, 4, 5 ...) db connections.
If your bases is not MySql only, you can use PDO extension:
$db1 = new PDO($param1, $param2, $param3);
$db2 = new PDO($param4, $param5, $param6);
And then you can use any of them, e.g.:
$db2->exec();
I am creating a web site using php, mysql and zend framework.
When I try to run any sql query, page generation jumps to around 0.5 seconds. That's too high. If i turn of sql, page generation is 0.001.
The amount of queries I run, doesn't really affect the page generation time (1-10 queries tested). Stays at 0.5 seconds
I can't figure out, what I am doing wrong.
I connect to sql in bootstrap:
protected function _initDatabase ()
{
try
{
$config = new Zend_Config_Ini( APPLICATION_PATH . '/configs/application.ini', APPLICATION_ENV );
$db = Zend_Db::factory( $config -> database);
Zend_DB_Table_Abstract::setDefaultAdapter( $db );
}
catch ( Zend_Db_Exception $e )
{
}
}
Then I have a simple model
class StandardAccessory extends Zend_DB_Table_Abstract
{
/**
* The default table name
*/
protected $_name = 'standard_accessory';
protected $_primary = 'model';
protected $_sequence = false;
}
And finally, inside my index controller, I just run the find method.
require_once APPLICATION_PATH . '/models/StandardAccessory.php';
$sa = new StandardAccessory( );
$stndacc = $sa->find( 'abc' );
All this takes ~0.5 seconds, which is way too long. Any suggestions?
Thanks!
Tips:
Cache the table metadata. By default, Zend_Db_Table tries to discover metadata about the table each time your table object is instantiated. Use a cache to reduce the number of times it has to do this. Or else hard-code it in your Table class (note: db tables are not models).
Use EXPLAIN to analyze MySQL's optimization plan. Is it using an index effectively?
mysql> EXPLAIN SELECT * FROM standard_accessory WHERE model = 'abc';
Use BENCHMARK() to measure the speed of the query, not using PHP. The subquery must return a single column, so be sure to return a non-indexed column so the query has to touch the data instead of just returning an index entry.
mysql> SELECT BENCHMARK(1000,
(SELECT nonindexed_column FROM standard_accessory WHERE model = 'abc'));
Note that Zend_Db_Adapter lazy-loads its db connection when you make the first query. So if there's any slowness in connecting to the MySQL server, it'll happen as you instantiate the Table object (when it queries metadata). Any reason this could take a long time? DNS lookups, perhaps?
The easiest way to debug this, is to profile your sql queries. you can use Firephp (plugin for firebug) see http://framework.zend.com/manual/en/zend.db.profiler.html#zend.db.profiler.profilers.firebug
another way to speed up things a little is to cache the metadata of your tables.
see: http://framework.zend.com/manual/en/zend.db.table.html#zend.db.table.metadata.caching
Along with the above suggestions I did a very unscientific test and found that the PDO adapter was faster for me in my application (I know mysqli is supposed to be faster but maybe it's the ZF abstraction). I show the results here (the times shown are only good for comparison)
I have a sqlite3 database on my harddrive (file.db) with 5 tables.
I'd like to copy 3 of these tables to an in-memory database (:memory:).
Is there a simple way to do so using PHP5's PDO format?
Not a pdo-specific solution that may or may not be sufficient in your case:
create a :memory: database
Attach the existing database file
CREATE TABLE ... AS SELECT * FROM ...
Detach the database file
edit: an example
First an example database stored in mydb.sq3
<?php
$pdo = new PDO('sqlite:mydb.sq3');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->exec('CREATE TABLE foo(x INTEGER PRIMARY KEY ASC, y, z)');
$stmt = $pdo->prepare("INSERT INTO foo (x,y,z) VALUES (:x,:y,:z)");
$stmt->bindParam(':x', $x);
$stmt->bindParam(':y', $y);
$stmt->bindParam(':z', $z);
for($x=0; $x<100; $x++) {
$y = $x*2;
$z = $x*2+1;
$stmt->execute();
}
Now we have a :memory: database and want to transfer the table foo
<?php
$pdo = new PDO('sqlite::memory:');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->exec('ATTACH "mydb.sq3" as filedb');
$pdo->exec('CREATE TABLE bar AS SELECT * FROM filedb.foo');
$pdo->exec('DETACH filedb');
Done. But let's take a look at the sqlite_master table
foreach($pdo->query('SELECT sql FROM sqlite_master') as $row) {
echo $row['sql'];
}
this prints
CREATE TABLE bar(x INT,y,z)
The INTEGER PRIMARY KEY ASC declaration is lost. Might be sufficient though....
If that's what you need to do, then VolkerK's answer is the one I'd provide, but I feel that I have to point out that you're going to read the contents of those tables into memory each time you run that code (every time that page loads?), so it might be better just to query the data files from disk.
Note that one could always use some kind of shared memory mechanism (e.g. APC, memcache, etc..) to keep sqlite's in-memory databases persistent across connections.
You can dump the database at the end of the connection, save it as apc variable and then load and run again from apc at the beginning of the next execution.
Using the method outlined by VolkerK roughly doubled the performance of my code when using a ~150Mb sqlite database.
Loading the database into an in-memory sqlite db didn't require any other changes to my existing code.
My use case was batch processing data so I didn't have to deal with the problems Wez Furlong highlights.
Reading the data into memory was surprisingly fast. The whole 150Mb was loaded into memory from SSD in less than two seconds. It seemed too good to be true so I checked and rechecked the data.
Many thanks to VolkerK for a magic solution!
I also found that when I indexed the in-memory tables my queries executed three times faster
//adapting VolkerK's example...
//loop through tables from the local sqlite db
$tables = array('companies', 'directors', 'previous_names');
foreach($tables as $table){
//load each table into memory
$pdo->exec("CREATE TABLE $table AS SELECT * FROM filedb.$table");
//index each table on the relevant columns
$pdo->exec("CREATE INDEX IF NOT EXISTS `".$table."_company_number`
ON $table (`company_number`);");
}
If you need a small database for tests, you may export your databse to an SQL file and then execute it as a single query in PHP:
class EphemeralPDO extends \PDO {
public function __construct() {
parent::__construct('sqlite::memory:', null, null, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
$queries = file_get_contents(__DIR__ . '/database.export.sql');
$this->exec($queries);
}
}