Cassandra (CQL) select statement with 'where' is not working - php

I am using Cassandra last few days. I am using PHPCassa library for that.
When I am trying to use the following code, Its not working correctly.
require_once('phpcassa/connection.php');
require_once "phpcassa/columnfamily.php";
// Create new ConnectionPool like you normally would
$pool = new ConnectionPool("newtest");
// Retrieve a raw connection from the ConnectionPool
$raw = $pool->get();
$rows = $raw->client->execute_cql_query("SELECT * FROM User WHERE KEY='phpqa'", cassandra_Compression::NONE);
echo "<pre>";
print_r($rows);
echo "<pre>";
// Return the connection to the pool so it may be used by other callers. Otherwise,
// the connection will be unavailable for use.
$pool->return_connection($raw);
unset($raw);
Its returning nothing, I have also tried following queries
$rows = $raw->client->execute_cql_query("SELECT * FROM User WHERE age='32'", cassandra_Compression::NONE);
$rows = $raw->client->execute_cql_query("SELECT * FROM User WHERE name='jack'", cassandra_Compression::NONE);
But When I tried
$rows = $raw->client->execute_cql_query("SELECT * FROM User", cassandra_Compression::NONE);
Its given the correct answer, displayed all the rows. Please advise me, how to use 'WHERE' properly.
Keyspace Details
Strategy Class: org.apache.cassandra.locator.SimpleStrategy
Strategy Options: None
Replication Factor: 1
Ring
Start Token: 6064078270600954295
End Token: 6064078270600954295
Endpoints: 127.0.0.1

In cassandra you cant just query a 'table' as you would normally. You need to set up secondary indexes for every column you might want to query.
Say we have a table:
key | User | Age
----------+----------------
phpqa | Jack | 20
You can query directly on the key:
SELECT * FROM User WHERE key='phpqa';
But to carry out other WHERE queries you'd need a secondary index on the columns you want to have available in the WHERE clause.
What can you do to make your querying flexible in the way that you desire:
Secondary indexes as described above.
Use composite columns as your key. Its a good idea if you only have 2-3 columns you want to query, but have a read through this article detailing how and when to use composite keys, and here is a link in how to implement it in phpcassa.

Add 'name' and 'age' as secondary indexes:
CREATE INDEX name_key on User( name );
CREATE INDEX age_key on User( age );
Then you should be able to use your select statement(s).
Read more here.

you are using a reserved word as a column name:
http://dev.mysql.com/doc/refman/5.5/en/reserved-words.html
$raw->client->execute_cql_query("SELECT * FROM User WHERE KEY='phpqa'",
cassandra_Compression::NONE)

Related

PHP - Left join from two tables in different databases with different credentials using PDO

Please note: While my original issue was not possible to be solved in the way I expected, #Bamar solution marked in this post is an alternative that reaches the same goal and works perfectly. What I proposed in this post to be done doesn't seem to be viable if the databases are located in different hosts.
I've been searching for a while and I seem to be unable to solve my issue.
THE DATA I HAVE
My service provider is 1&1. In the current contract I have with them I could create up to 100 databases with a maximun size of 2GB each.
Each database that is created, is assingned a random hostname, port and username (the only item which I can choose is the password).
I've got two different databases, lets call them DB_1 and DB_2.
In the DB_1 I've got a table called T_USERS which fields of interest for this particular problem are:
ID: The ID of the record.
userName: The user name registered on the database.
In the DB_2 I've got a table called T_SCORES which fields of interest for this particular problem are:
ID_User: it's a foregin key that refers to the ID of a particular user in DB_1.T_USERS
score: a numeric value that indicates the score of that user.
It is important to take into account that to access both databases each of them needs different credentials!
WHAT I WANT TO ACHIEVE
What I want to achieve seems simple at a first glance but I was unable to find any documentation or solution online on how to do this using PHP and PDO.
I just want to perform a join with DB_2.ID_USER and DB_1.ID
My final result should look something like this:
DB_1.userName
DB_2.score
Alex
237
Peter
120
Mark
400
...
...
WHERE I'M CURRENTLY STUCK
This is what I've currently tried.
First of all I perform the connection to my databases as follows (I normally use a try/catch when connecting to a DB but I will omit it here):
//Connection to the DB1
$db1_hostName = "hostnameofDB1";//The host name of the database 1
$db1_name = "db1";//The name of the database 1
$db1_userName = "user1";//The username in the database 1
$db1_password = "pw1";//The password for the database 1
$pdo_db1Handle = new PDO("mysql:host=$db1_hostName; dbname=$db1_name;", $db1_userName, $db1_password);
$pdo_db1Handle->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
//Connection to the DB2
$db2_hostName = "hostnameofDB2";//The host name of the database 2
$db2_name = "db2";//The name of the database 2
$db2_userName = "user2";//The username in the database 2
$db2_password = "pw2";//The password for the database 2
$pdo_db2Handle = new PDO("mysql:host=$db2_hostName; dbname=$db2_name;", $db2_userName, $db2_password);
$pdo_db2Handle->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
So basically up to this point what I've done is very simple, create a pdo_db1Handle and pdo_db2Handle. Now to the tricky part...
If I now want to perform a join my SQL syntax should be something like this:
SELECT DB_1.T_USERS.userName, DB_2.T_SCORES.score
FROM DB_2.T_SCORES
LEFT JOIN DB_1.T_USERS
ON (DB_2.T_SCORES.ID_User=DB_1.T_USERS.ID)
ORDER BY DB_2.T_SCORES.score ASC 'The ordering is optional, I'm interested in the join part first
But as far as I'm aware and with all the information I was able to find, you execute the SQL statement against one of the two handles I previously defined in the following way:
$stmt=$pdo_db1Handle->prepare($mySQLStatement);
$stmt->execute();
When I try to do this, an error shows up telling me missing credentials for the DB_2. It happens the opposite (missing credentials of DB_1) if I try to execute it against pdo_db2Handle.
How should I proceed? any solution using PDO for this?
Thanks in advance :)
You can't join if you have to use separate PDO connections, so use nested loops and join the data in PHP.
$stmt_user = $pdo_db1Handle->query("SELECT id, username FROM t_users");
$stmt_score = $pdo_db2Handle->prepare("SELECT score FROM t_scores WHERE id_user = :userid");
$results = [];
while ($row_user = $stmt_user->fetch(PDO::FETCH_ASSOC)) {
$scores = [];
$stmt_score->execute(':userid' => $row_user['id']);
while ($row_score = $stmt_score->fetch(PDO::FETCH_ASSOC)) {
$scores[] = $row_score['score'];
}
$results[$row_user['username']] = $scores;
}
This will create an associative array whose keys are usernames and values are an array of their scores.
Depending on your use case, a work around may be to copy the table from one database to another temporarily and the perform your sql once you have both tables in a single database:
$pdo1 = new PDO('mysql:host=$db1_hostName; dbname=$db1_name', $db1_userName, $db1_password);
$pdo2 = new PDO('mysql:host=$db2_hostName; dbname=$db1_name', $db2_userName, $db2_password);
$insert_stmt = $pdo2->prepare("INSERT INTO T_SCORES (col1, col2, col3, ...) VALUES (:col1, :col2, :col3, ...) ON DUPLICATE KEY IGNORE");
$select_results = $pdo1->query("SELECT * FROM T_SCORES");
while ($row = $select_results->fetch(PDO::FETCH_ASSOC)) {
$insert_stmt->execute($row);
}
-- now work with the tables as you usually would.
You can create the table in the target database before hand and truncate the data before and/or after performing the insert.

How to get a column's default value using Laravel?

I am building a custom artisan command that needs to be able to access the database's default values for certain columns. I cannot use the attributes array. So instead I need another way.
I have tried to use Schema. I have been able to get the table DB::table($table) and the column names Schema::getColumnListings($table) but not the default values.
Is there some other way to get the default values?
The Laravel Schema Builder only returns column names by design. But you can use the same approach Laravel uses internally by executing a database statement:
$results = DB::select('
select column_default
from information_schema.columns
where
table_schema = ?
and table_name = ?
', [$database, $table]);
// Flatten the results to get an array of the default values
$defaults = collect($results)->pluck('column_default'))
The above example works for a MySQL database, but you can see the approaches for other databases in the Illuminate\Database\Schema\Grammars namespace of the Laravel source code by searching for the method compileColumnListing.
In Laravel 5+ (including 6 and 7), you can get the db table column metadata (ie type, default value etc) in the following way:
use Illuminate\Support\Facades\Schema;
For all columns:
$columns = Schema::getConnection()->getDoctrineSchemaManager()->listTableColumns('table_name');
For a single column:
$column = Schema::getConnection()->getDoctrineColumn('table_name'', 'column_name');
getDoctrineSchemaManager method returns a array of \Doctrine\DBAL\Schema\Column Class Instances. By using this you can get everything about a db table column.
getDoctrineColumn method returns the instance of \Doctrine\DBAL\Schema\Column class.
Couple of methods from \Doctrine\DBAL\Schema\Column class:
$column->getName();
$column->getNotnull(); // returns true/false
$column->getDefault();
$column->getType();
$column->getLength();

CONCAT columns with Laravel 5 eloquent

Consider me as laravel beginner
The goal is: I have two colums, now I need the id to be prefixed with the component name of same row in the table.
For Example (Working)... I have Mysql like
SELECT CONCAT(components.name," ", components.id) AS ID
FROM `components`
And output is
ID
|TestComp 40 |
-------------
|component 41 |
-------------
|test 42 |
I need the same in laravel eloquent way, as here Component is Model name. So i tried something like
$comp=Component::select("CONCAT('name','id') AS ID")->get()
but it doesn't work.
I think because the syntax is wrong.
Kindly help me with the correct syntax. Using laravel Models.
Note: I made the above query, referring this as which available on internet.
User::select(DB::raw('CONCAT(last_name, first_name) AS full_name'))
You need to wrap your query in DB::raw:
$comp = Component::select(DB::raw("CONCAT('name','id') AS ID"))->get()
Also, note because you are doing your query like this, your model might behave differently, because this select removes all other fields from the select statement.
So you can't read the other fields from your model without a new query. So ONLY use this for READING data and not MODIFYING data.
Also, to make it in a nice list, I suggest you modify your query to:
$comp = Component::select(DB::raw("CONCAT('name','id') AS display_name"),'id')->get()->pluck('display_name','id');
// dump output to see how it looks.
dd($comp);// array key should be the arrray index, the value the concatted value.
I came to this post for answers myself. The only problem for me is that the answer didn't really work for my situation. I have numerous table relationships setup and I needed one of the child objects to have a concatenated field. The DB::raw solution was too messy for me. I kept searching and found the answer I needed and feel it's an easier solution.
Instead of DB::raw, I would suggest trying an Eloquent Accessor. Accessors allow you to retrieve model attributes AND to create new ones that are not created by the original model.
For instance, let's say I have a basic USER_PROFILE table. It contains id, first_name, last_name. I have the need to CONCAT the two name attributes to return their user's full name. In the USER_PROFILE Model I created php artisan make:model UserProfile, I would place the following:
class UserProfile extends Model
{
/**
* Get the user's full concatenated name.
* -- Must postfix the word 'Attribute' to the function name
*
* #return string
*/
public function getFullnameAttribute()
{
return "{$this->first_name} {$this->last_name}";
}
}
From here, when I make any eloquent calls, I now have access to that additional attribute accessor.
| id | first_name | last_name |
-------------------------------
| 1 | John | Doe |
$user = App\UserProfile::first();
$user->first_name; /** John **/
$user->fullname; /** John Doe **/
I will say that I did run into one issue though. That was trying to create a modified attribute with the same name, like in your example (id, ID). I can modify the id value itself, but because I declared the same name, it appears to only allow access to that field value and no other field.
Others have said they can do it, but I was unable to solve this questions EXACT problem.
I working on posgresql and mysql:
DB::raw('CONCAT(member.last_name, \' \', member.first_name) as full_name')
$text = "other";
$limit = 100
public function get_data($text, $limit)
{
$result = $this->select('titulo', 'codigo', DB::Raw("CONCAT(codigo, ' ', titulo_long) AS text_search"))
->where('tipo', '=', 2)
->having('text_search', 'LIKE', "%$text%")
->limit($limit)
->get();
return $result;
}
}
Here is the example of columns concatenation in Laravel.
I need to search the user by name and I have three columns for the user name (name_first, name_middle, name_last), so I have created a scope in Laravel UserModel which takes $query and user name as the second parameter.
public function scopeFindUserByName($query,$name) {
// Concat the name columns and then apply search query on full name
$query->where(DB::raw(
// REPLACE will remove the double white space with single (As defined)
"REPLACE(
/* CONCAT will concat the columns with defined separator */
CONCAT(
/* COALESCE operator will handle NUll values as defined value. */
COALESCE(name_first,''),' ',
COALESCE(name_middle,''),' ',
COALESCE(name_last,'')
),
' ',' ')"
),
'like', '%' . $name . '%');
}
and you can use this scope anywhere you need to search user by his name, like
UserModel::findUserByName("Max Begueny");
OR
$query = UserModel::query();
$query->findUserByName("Max Begueny");
To check the result of this SQL query just go through from this post.
https://stackoverflow.com/a/62296860/11834856
this code should work:
User::select(\DB::raw('CONCAT(last_name, first_name) AS full_name)')
Scrubbing the Data: Before using Laravel and now, when developing in other languages, I would use CONCAT() on a regular basis. The answers here work to a degree but there still isn't an elegant way to use CONCAT() in Laravel/Eloquent/Query Builder
that I have found.
However, I have found that concatenating the cols AFTER returning the results works well for me and is usually very fast - Scrubbing the data - ( unless you have a huge result which should probably be "chunked" anyway for performance purposes ).
foreach($resultsArray AS $row){
$row['fullname'] = trim($row['firstname']).' '.trim($row['lastname']);
}
This is a tradeoff of course but, personally, I find it to be much more manageable and doesn't limit my use of Eloquent as intended as well as the Query Builder. ( the above is pseudo code - not tested so tweak as needed )
There are other workarounds as well that don't mess with Eloquent/Query Builder functionality such as creating a concatenated col in the table, in this case full_name - save the full name when the record is inserted/updated. This is not uncommon.

Laravel - multi-insert rows and retrieve ids

I'm using Laravel 4, and I need to insert some rows into a MySQL table, and I need to get their inserted IDs back.
For a single row, I can use ->insertGetId(), however it has no support for multiple rows. If I could at least retrieve the ID of the first row, as plain MySQL does, would be enough to figure out the other ones.
It's mysql behavior of
last-insert-id
Important
If you insert multiple rows using a single INSERT statement, LAST_INSERT_ID() returns the value generated for the first inserted row only. The reason for this is to make it possible to reproduce easily the same INSERT statement against some other server.
u can try use many insert and take it ids or after save, try use $data->id should be the last id inserted.
If you are using INNODB, which supports transaction, then you can easily solve this problem.
There are multiple ways that you can solve this problem.
Let's say that there's a table called Users which have 2 columns id, name and table references to User model.
Solution 1
Your data looks like
$data = [['name' => 'John'], ['name' => 'Sam'], ['name' => 'Robert']]; // this will insert 3 rows
Let's say that the last id on the table was 600. You can insert multiple rows into the table like this
DB::begintransaction();
User::insert($data); // remember: $data is array of associative array. Not just a single assoc array.
$startID = DB::select('select last_insert_id() as id'); // returns an array that has only one item in it
$startID = $startID[0]->id; // This will return 601
$lastID = $startID + count($data) - 1; // this will return 603
DB::commit();
Now, you know the rows are between the range of 601 and 603
Make sure to import the DB facade at the top using this
use Illuminate\Support\Facades\DB;
Solution 2
This solution requires that you've a varchar or some sort of text field
$randomstring = Str::random(8);
$data = [['name' => "John$randomstring"], ['name' => "Sam$randomstring"]];
You get the idea here. You add that random string to a varchar or text field.
Now insert the rows like this
DB::beginTransaction();
User::insert($data);
// this will return the last inserted ids
$lastInsertedIds = User::where('name', 'like', '%' . $randomstring)
->select('id')
->get()
->pluck('id')
->toArray();
// now you can update that row to the original value that you actually wanted
User::whereIn('id', $lastInsertedIds)
->update(['name' => DB::raw("replace(name, '$randomstring', '')")]);
DB::commit();
Now you know what are the rows that were inserted.
As user Xrymz suggested, DB::raw('LAST_INSERT_ID();') returns the first.
According to Schema api insertGetId() accepts array
public int insertGetId(array $values, string $sequence = null)
So you have to be able to do
DB::table('table')->insertGetId($arrayValues);
Thats speaking, if using MySQL, you could retrive the first id by this and calculate the rest. There is also a DB::getPdo()->lastInsertId(); function, that could help.
Or if it returened the last id with some of this methods, you can calculate it back to the first inserted too.
EDIT
According to comments, my suggestions may be wrong.
Regarding the question of 'what if row is inserted by another user inbetween', it depends on the store engine. If engine with table level locking (MyISAM, MEMORY, and MERGE) is used, then the question is irrevelant, since thete cannot be two simultaneous writes to the table.
If row-level locking engine is used (InnoDB), then, another possibility might be to just insert the data, and then retrieve all the rows by some known field with whereIn() method, or figure out the table level locking.
$result = Invoice::create($data);
if ($result) {
$id = $result->id;
it worked for me
Note: Laravel version 9

Select a text field from mysql in php

usersim interested how do i select a text field form my mysql database, i have a table named users with a text field called "profile_fields" where addition user info is stored. How do i access it in php and make delete it? I want to delete unvalidate people.
PHP code
<?php
//Working connection made before assigned as $connection
$time = time();
$query_unactive_users = "DELETE FROM needed WHERE profile_fields['valid_until'] < $time"; //deletes user if the current time value is higher then the expiring date to validate
mysqli_query($connection , $query_unactive_users);
mysqli_close($connection);
?>
In phpmyadmin the field shows (choosen from a random user row):
a:1:{s:11:"valid_until";i:1370695666;}
Is " ... WHERE profile_fields['valid_until'] ..." the correct way?
Anyway, here's a very fragile solution using your knowledge of the string structure and a bit of SUBSTRING madness:
DELETE FROM needed WHERE SUBSTRING(
profile_fields,
LOCATE('"valid_until";i:', profile_fields) + 16,
LOCATE(';}', profile_fields) - LOCATE('"valid_until";i:', profile_fields) - 16
) < UNIX_TIMESTAMP();
But notice that if you add another "virtual field" after 'valid_until', that will break...
You can't do it in a SQL command in a simple and clean way. However, the string 'a:1:{s:11:"valid_until";i:1370695666;}' is simply a serialized PHP array.
Do this test:
print_r(unserialize('a:1:{s:11:"valid_until";i:1370695666;}'));
The output will be:
Array ( [valid_until] => 1370695666 )
So, if you do the following, you can retrieve your valid_until value:
$arrayProfileData = unserialize('a:1:{s:11:"valid_until";i:1370695666;}');
$validUntil = arrayProfileData['valid_until'];
So, a solution would be to select ALL items in the table, do a foreach loop, unserialize each "profile_fields" field as above, check the timestamp, and store the primary key of each registry to be deleted, in a separate array. At the end of the loop, do a single DELETE operation on all primary keys you stored in the loop. To do that, use implode(',', $arrayPKs).
It's not a very direct route, and depending on the number of registers, it may not be slow, but it's reliable.
Consider rixo's comment: if you can, put the "valid_until" in a separate column. Serializing data can be good for storage of non-regular data, but never use it to store data which you may need to apply SQL filters later.

Categories