PHP values inside pdo->query or/and prepared statements - pdo

I'm limiting the amount of comments shown on the page by 2, $second_count counts how many posts are if more than 2 it limits and show's a Show All comments, that's what this is for.
If you look into the variable $limitPost, how do I add parameter inside it.
This is what I'm trying to accomplish but with PDO.
$limitPost = "DESC LIMIT $second_count,2"; but this can lead to SQL INJECTIONS to my understanding.
PUBLIC FUNCTION userComments($post_iD,$second_count)
{
$limitPost = "DESC LIMIT 2";
$sth = $this->db->prepare("SELECT C.com_id, C.uid_fk, C.comment, C.created, U.username, U.photo
FROM comments C, users U
WHERE U.status='1' AND C.uid_fk = U.uiD
AND C.msg_id_fk = :postiD
ORDER BY C.com_id < :second_count");
$sth->bindParam(':postiD', $post_iD, PDO::PARAM_INT);
$sth->bindParam(':second_count', $limitPost, PDO::PARAM_INT);
$sth->execute();
$data = $sth->fetchAll();
return $data;
}
UPDATE
This is what $second_count is, it it's just counting if there are 2 comments showing, it'll hide all the rest and if I press show all comments it'll expand.
<?php
$x=1;
if($x){
$comment_count = count($commentsarray);
$second_count = $comment_count-2;
if($comment_count>2){
?>
<div class="comment_ui" id="view<?php echo $post_iD;?>">
Show all <?php echo $comment_count;?> comments
</div>
<?php
$commentsarray = $Wall->userComments($post_iD, $second_count);
}
}
?>

alright:
If you look into the variable $limitPost, how do I add another
variable inside it.
This is what I'm trying to accomplish but with PDO.
impossible <- that is the answer to your question

instead of :
:second_count
try:
DESC LIMIT 2
into the prepared statement. You really do not need the parameter :second_count in your prepared statement since it is a constant string.
SQL injections would be possible if :second_count was an input by a user, since is it constant, nothing like that is possible.
Why what you are doing is not working:
DESC LIMIT 2 is not an integer parameter, so the bind should fail or give out junk.
If you were to use PDO::PARAM_STR on the bind, :second_count would be put into qoutes, so the sql statement would fail.

Related

SQL Redshift - count number of times column A value appears in column B value [duplicate]

I am wanting to count all occurrences of the # symbol in a field and originally i thought LIKE '%#%' would be the way to go, but if the character appears in the field more than once it only counts it as one.
What other method are there that i could use that would count every occurrence?
Thanks.
EDIT
For anyone needing it, this is what i ended up using that works.
$count = 0;
$sql = mysql_query("SELECT LENGTH(field_name) - LENGTH(REPLACE(field_name,'#','')) AS 'occurs' FROM table_name WHERE field_name LIKE '%#%'");
while ($data = mysql_fetch_assoc($sql)) {
$count += $data['occurs'];
}
echo $count;
select length('aa:bb:cc:dd')-length(replace('aa:bb:cc:dd',':',''));
source: http://lists.mysql.com/mysql/215049
You could make this even simpler by using the ``substr_count function in php. see below.
$message = $row['themessage'];
echo substr_count($message, '#');
what this will return is the number of times # has occurred in your "themessage" field in your database.

How to select from subquery using Laravel Query Builder?

I'd like to get value by the following SQL using Eloquent ORM.
- SQL
SELECT COUNT(*) FROM
(SELECT * FROM abc GROUP BY col1) AS a;
Then I considered the following.
- Code
$sql = Abc::from('abc AS a')->groupBy('col1')->toSql();
$num = Abc::from(\DB::raw($sql))->count();
print $num;
I'm looking for a better solution.
Please tell me simplest solution.
In addition to #delmadord's answer and your comments:
Currently there is no method to create subquery in FROM clause, so you need to manually use raw statement, then, if necessary, you will merge all the bindings:
$sub = Abc::where(..)->groupBy(..); // Eloquent Builder instance
$count = DB::table( DB::raw("({$sub->toSql()}) as sub") )
->mergeBindings($sub->getQuery()) // you need to get underlying Query Builder
->count();
Mind that you need to merge bindings in correct order. If you have other bound clauses, you must put them after mergeBindings:
$count = DB::table( DB::raw("({$sub->toSql()}) as sub") )
// ->where(..) wrong
->mergeBindings($sub->getQuery()) // you need to get underlying Query Builder
// ->where(..) correct
->count();
Laravel v5.6.12 (2018-03-14) added fromSub() and fromRaw() methods to query builder (#23476).
The accepted answer is correct but can be simplified into:
DB::query()->fromSub(function ($query) {
$query->from('abc')->groupBy('col1');
}, 'a')->count();
The above snippet produces the following SQL:
select count(*) as aggregate from (select * from `abc` group by `col1`) as `a`
The solution of #JarekTkaczyk it is exactly what I was looking for. The only thing I miss is how to do it when you are using
DB::table() queries. In this case, this is how I do it:
$other = DB::table( DB::raw("({$sub->toSql()}) as sub") )->select(
'something',
DB::raw('sum( qty ) as qty'),
'foo',
'bar'
);
$other->mergeBindings( $sub );
$other->groupBy('something');
$other->groupBy('foo');
$other->groupBy('bar');
print $other->toSql();
$other->get();
Special atention how to make the mergeBindings without using the getQuery() method
From laravel 5.5 there is a dedicated method for subqueries and you can use it like this:
Abc::selectSub(function($q) {
$q->select('*')->groupBy('col1');
}, 'a')->count('a.*');
or
Abc::selectSub(Abc::select('*')->groupBy('col1'), 'a')->count('a.*');
There are many readable ways to do these kinds of queries at the moment (Laravel 8).
// option 1: DB::table(Closure, alias) for subquery
$count = DB::table(function ($sub) {
$sub->from('abc')
->groupBy('col1');
}, 'a')
->count();
// option 2: DB::table(Builder, alias) for subquery
$sub = DB::table('abc')->groupBy('col1');
$count = DB::table($sub, 'a')->count();
// option 3: DB::query()->from(Closure, alias)
$count = DB::query()
->from(function ($sub) {
$sub->from('abc')
->groupBy('col1')
}, 'a')
->count();
// option 4: DB::query()->from(Builder, alias)
$sub = DB::table('abc')->groupBy('col1');
$count = DB::query()->from($sub, 'a')->count();
For such small subqueries, you could even try fitting them in a single line with PHP 7.4's short closures but this approach can be harder to mantain.
$count = DB::table(fn($sub) => $sub->from('abc')->groupBy('col1'), 'a')->count();
Note that I'm using count() instead of explicitly writing the count(*) statement and using get() or first() for the results (which you can easily do by replacing count() with selectRaw(count(*))->first()).
The reason for this is simple: It returns the number instead of an object with an awkwardly named property (count(*) unless you used an alias in the query)
Which looks better?
// using count() in the builder
echo $count;
// using selectRaw('count(*)')->first() in the builder
echo $count->{'count(*)'};
Correct way described in this answer: https://stackoverflow.com/a/52772444/2519714
Most popular answer at current moment is not totally correct.
This way https://stackoverflow.com/a/24838367/2519714 is not correct in some cases like: sub select has where bindings, then joining table to sub select, then other wheres added to all query. For example query:
select * from (select * from t1 where col1 = ?) join t2 on col1 = col2 and col3 = ? where t2.col4 = ?
To make this query you will write code like:
$subQuery = DB::query()->from('t1')->where('t1.col1', 'val1');
$query = DB::query()->from(DB::raw('('. $subQuery->toSql() . ') AS subquery'))
->mergeBindings($subQuery->getBindings());
$query->join('t2', function(JoinClause $join) {
$join->on('subquery.col1', 't2.col2');
$join->where('t2.col3', 'val3');
})->where('t2.col4', 'val4');
During executing this query, his method $query->getBindings() will return bindings in incorrect order like ['val3', 'val1', 'val4'] in this case instead correct ['val1', 'val3', 'val4'] for raw sql described above.
One more time correct way to do this:
$subQuery = DB::query()->from('t1')->where('t1.col1', 'val1');
$query = DB::query()->fromSub($subQuery, 'subquery');
$query->join('t2', function(JoinClause $join) {
$join->on('subquery.col1', 't2.col2');
$join->where('t2.col3', 'val3');
})->where('t2.col4', 'val4');
Also bindings will be automatically and correctly merged to new query.
I like doing something like this:
Message::select('*')
->from(DB::raw("( SELECT * FROM `messages`
WHERE `to_id` = ".Auth::id()." AND `isseen` = 0
GROUP BY `from_id` asc) as `sub`"))
->count();
It's not very elegant, but it's simple.
This works fine
$q1 = DB::table('tableA')->groupBy('col');
$data = DB::table(DB::raw("({$q1->toSql()}) as sub"))->mergeBindings($q1)->get();
I could not made your code to do the desired query, the AS is an alias only for the table abc, not for the derived table.
Laravel Query Builder does not implicitly support derived table aliases, DB::raw is most likely needed for this.
The most straight solution I could came up with is almost identical to yours, however produces the query as you asked for:
$sql = Abc::groupBy('col1')->toSql();
$count = DB::table(DB::raw("($sql) AS a"))->count();
The produced query is
select count(*) as aggregate from (select * from `abc` group by `col1`) AS a;
->selectRaw('your subquery as somefield')
Deriving off mpskovvang's answer, here is what it would look like using eloquent model. (I tried updating mpskovvang answer to include this, but there's too many edit requests for it.)
$qry = Abc::where('col2', 'value')->groupBy('col1')->selectRaw('1');
$num = Abc::from($qry, 'q1')->count();
print $num;
Produces...
SELECT COUNT(*) as aggregate FROM (SELECT 1 FROM Abc WHERE col2='value' GROUP BY col1) as q1

PDO Riddle (No while loop)

I just recently learned how to work with PDO database queries, but my latest query isn't working for some reason. All my other queries include arrays and while loops, but I don't think this one requires either, so I may just need to change the query structure somehow.
$stm = $pdo->prepare("SELECT H.N, H.URL, H.Title, H.Subtitle,
H.Site, H.MetaTitle, H.MetaDesc, H.KW, H.Live, A.Article, A.Pagedex
FROM 1_home_pages H
LEFT JOIN 1_home_articles A ON A.Site = H.Site
WHERE H.URL = :MySection AND H.Site = :MySiteId AND H.Live = 1 AND A.Site = :MySiteId AND A.Section = :MySection");
// $stm->execute();
$stm->execute(array(
'MySiteId'=>$MySiteID,
'MySection'=>$MySection
));
$data = $stm->fetchAll();
I added the line $data = $stm->fetchAll();, and it now works - as an array. The foreach statement below displays EVERYTHING in one jumbled mess...
foreach($data as $row) {
print_r($row);
}
To escape the array and just display one item at a time, I tried the following:
$Content = $data['Article'];
echo $Content;
But it doesn't display anything. Can anyone tell me the solution?
I don't know if you made a typo while posting this question, or this is a typo from your source code: you have :MySection and yet while executing you write MySection (without colon). What's more, this line:
WHERE H.URL = :MySection
AND H.Site = :MySiteId
AND H.Live = 1
AND A.Site = :MySiteId
AND A.Section = :MySection");
You used multiple parameters with same name in one statement. Don't know if this is a correct way to do it, since I've never done it. I suggest you try using different names for parameters and see if that helps. Also, what do you mean 'but my latest query isn't working for some reason.' ? What does it return? Does it return anything at all? fetchAll returns multiple rows, that's why you get array.
Instead of $data = $stm->fetchAll(); use $data = $stm->fetch();. It will return next row from your query.

How to manage duplicate sql values that has to be unique?

Users of my site can submit post title and post content by form. The post title will be saved and converted to SEO friendly mode (eg: "title 12 $" -> "title-12"). this will be this post's url.
My question is if a user entered a title that is identical to previous entered title, the url's of those posts will be identical. So can be the new title be modified automatically by appending a number to the end of the title?
eg:
"title-new" -> "title-new-1" or if "title-new-1" present in db
convert it to "title-new-2"
I'm sorry I'm new to this, maybe it's very easy, Thanks for your help.
I'm using PDO.
when saving the post title you can query the db if it exists? If it exists the simply append a number and again query, if it again exists increment it by one and hence.
Do a select on your database for that title, and use rowCount() to check how many results you have. If the result is 0: you can add it, if the result is n, you add n to the title.
To append use something like this (not correct):
$count = $del->rowCount(); //Assuming this returns 1
if($count){
$title = $title . "-" . $count;
}
This will give you "theduplicatetitle-1"
thank you #Ratna & #Borniet for your answers. i'm posting explained code to any other user who want's it. if there is somrthing should be added or removed or better way please let me know.
//first i'm going to search the "new unchanged title name" whether it's present in db.
$newtitle= "tst-title";
$dbh = new PDO("mysql:host=localhost;dbname='dbname', 'usrname', 'password');
$stmt = $dbh->prepare("SELECT * FROM `tablename` WHERE `col.name` = ? LIMIT 1");
$stmt->execute(array($newtitle));
if ( $stmt->rowCount() < 1 ) {
// enter sql command to insert data
}
else {
$i='0';
do{
$i++;
$stmt = $dbh->prepare("SELECT * FROM `tablename` WHERE `url` = ? LIMIT 1");
$stmt->execute(array($newtitle.'-'.$i));
}
while($stmt->rowCount()>0);
// enter sql command to insert data
}
that's it. the reason i'm dividing in to two is because i want to add '-' to the url instead of just number.

SELECT MAX query returns only 1 variable + codeigniter

I use codeigniter and have an issue about SELECT MAX ... I couldnot find any solution at google search...
it looks like it returns only id :/ it's giving error for other columns of table :/
Appreciate helps, thanks!
Model:
function get_default()
{
$this->db->select_max('id');
$query = $this->db->getwhere('gallery', array('cat' => "1"));
if($query->num_rows() > 0) {
return $query->row_array(); //return the row as an associative array
}
}
Controller:
$default_img = $this->blabla_model->get_default();
$data['default_id'] = $default_img['id']; // it returns this
$data['default_name'] = $default_img['gname']; // it gives error for gname although it is at table
To achieve your goal, your desire SQL can look something like:
SELECT *
FROM gallery
WHERE cat = '1'
ORDER BY id
LIMIT 1
And to utilise CodeIgniter database class:
$this->db->select('*');
$this->db->where('cat', '1');
$this->db->order_by('id', 'DESC');
$this->db->limit(1);
$query = $this->db->get('gallery');
That is correct: select_max returns only the value, and no other column. From the specs:
$this->db->select_max('age');
$query = $this->db->get('members');
// Produces: SELECT MAX(age) as age FROM members
You may want to read the value first, and run another query.
For an id, you can also use $id = $this->db->insert_id();
See also: http://www.hostfree.com/user_guide/database/active_record.html#select
CodeIgniter will select * if nothing else is selected. By setting select_max() you are populating the select property and therefore saying you ONLY want that value.
To solve this, just combine select_max() and select():
$this->db->select('somefield, another_field');
$this->db->select_max('age');
or even:
$this->db->select('sometable.*', FALSE);
$this->db->select_max('age');
Should do the trick.
It should be noted that you may of course also utilize your own "custom" sql statements in CodeIgniter, you're not limited to the active record sql functions you've outlined thus far. Another active record function that CodeIgniter provides is $this->db->query(); Which allows you to submit your own SQL queries (including variables) like so:
function foo_bar()
{
$cat = 1;
$limit = 1;
$sql = "
SELECT *
FROM gallery
WHERE cat = $cat
ORDER BY id
LIMIT $limit
";
$data['query'] = $this->db->query($sql);
return $data['query'];
}
Recently I have been utilizing this quite a bit as I've been doing some queries that are difficult (if not annoying or impossible) to pull off with CI's explicit active record functions.
I realize you may know this already, just thought it would help to include for posterity.
2 helpful links are:
http://codeigniter.com/user_guide/database/results.html
http://codeigniter.com/user_guide/database/examples.html