26

In essence, I have a value that I have to call a couple times in my SQL query. Thus, is it possible to reuse the same named placeholder in the statement e.g. SELECT :Param FROM Table WHERE Column = :Param, then simply bindValue(":Param"), and have the value be there for both :Params?

j0k
  • 22,600
  • 28
  • 79
  • 90
gamers2000
  • 1,847
  • 2
  • 14
  • 15
  • Why don't you just try it? PDO::prepare says yes and PDOStatement::execute says you may need to close the db cursor dependent upon the driver. – Steve-o Mar 12 '10 at 10:58
  • I tried it, I kept getting an error, but I figured I was doing something else wrong. Was couldn't find anything in the docs (was looking at PDO::bindParam). Found it in PDO::prepare though,thanks for pointing me to the right direction! – gamers2000 Mar 15 '10 at 07:57
  • Actually, the first part `SELECT :Param` that is impossible; you can't bind tables/columns, consult http://stackoverflow.com/q/182287/. *However,* you could do `$Param = "column"; SELECT $Param...` – Funk Forty Niner Dec 13 '14 at 17:54
  • Do you think you could award me with the answer is that possible? The current accepted states it is not possible but it is with my solution – Jonathan May 29 '20 at 12:50

5 Answers5

23

PDO::prepare states that "you cannot use a named parameter marker of the same name twice in a prepared statement", so I guess that's a no then.

IMSoP
  • 89,526
  • 13
  • 117
  • 169
gamers2000
  • 1,847
  • 2
  • 14
  • 15
  • 13
    The docs are not completely correct. If `PDO::ATTR_EMULATE_PREPARES` is `true`, PDO *DOES* support reusing named parameter markers. – Mike Feb 12 '14 at 07:02
  • 4
    It looks like someone updated the docs. It now says "You cannot use a named parameter marker of the same name more than once in a prepared statement, unless emulation mode is on." – Mike Apr 22 '14 at 21:57
  • 2
    You're partially right. However `SELECT :Param` is impossible, since you can't bind tables/columns. Check out http://stackoverflow.com/q/182287/ – Funk Forty Niner Dec 13 '14 at 17:56
  • 1
    but then pdo turns everything into a string if you use `PDO::ATTR_EMULATE_PREPARES` – Gaby_64 Dec 16 '19 at 19:17
9

You can if you set PDO::ATTR_EMULATE_PREPARES = true.

E.g. $connection->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);.

If you're using Laravel you can set this in an options array in config/database.php. e.g. PDO::ATTR_EMULATE_PREPARES => true

Jonathan
  • 10,936
  • 8
  • 64
  • 79
3

Apart from reuse, the main issue here is that you are trying to dynamically change col names.

This answer posted by an anonymous user on http://php.net/manual/en/pdo.prepare.php :

To those wondering why adding quotes to around a placeholder is wrong, and why you can't use placeholders for table or column names:

There is a common misconception about how the placeholders in prepared statements work: they are not simply substituted in as (escaped) strings, and the resulting SQL executed. Instead, a DBMS asked to "prepare" a statement comes up with a complete query plan for how it would execute that query, including which tables and indexes it would use, which will be the same regardless of how you fill in the placeholders.

The plan for "SELECT name FROM my_table WHERE id = :value" will be the same whatever you substitute for ":value", but the seemingly similar "SELECT name FROM :table WHERE id = :value" cannot be planned, because the DBMS has no idea what table you're actually going to select from.

Even when using "emulated prepares", PDO cannot let you use placeholders anywhere, because it would have to work out what you meant: does "Select :foo From some_table" mean ":foo" is going to be a column reference, or a literal string?

When your query is using a dynamic column reference, you should be explicitly white-listing the columns you know to exist on the table, e.g. using a switch statement with an exception thrown in the default: clause.

frostymarvelous
  • 2,786
  • 32
  • 43
2

Many queries like yours can be rewritten to use only one placeholder.

SELECT :Param FROM Table WHERE Column = :Param

would be the same as

SELECT Column FROM Table WHERE Column = :Param

But sometimes it's not that simple. For example:

SELECT *
FROM my_table
WHERE first_name LIKE :Param
   OR last_name  LIKE :Param
   OR biography  LIKE :Param

In such case you could reuse the parameter value storing it in a cross joined derived table (subquery in FROM clause):

SELECT t.*
FROM my_table t
CROSS JOIN (SELECT :Param as Param) AS x
WHERE first_name LIKE x.Param
   OR last_name  LIKE x.Param
   OR biography  LIKE x.Param
Paul Spiegel
  • 30,925
  • 5
  • 44
  • 53
0

There's a workaround:

  function search($criteria) {

     $sql = "SELECT * FROM my_table
        WHERE column_1 like CONCAT('%', :criteria1, '%') 
           OR column_2 like CONCAT('%', :criteria2, '%') 
           OR column_3 like CONCAT('%', :criteria3, '%')  
     ";

     $stmt =  $this->db->prepare($sql);
     $stmt->bindParam(':criteria1', $criteria);
     $stmt->bindParam(':criteria2', $criteria);
     $stmt->bindParam(':criteria3', $criteria);
     $stmt->execute();
     return($stmt->fetchAll(PDO::FETCH_ASSOC));
  }

In summary, use different placeholders with the same criteria.

Manny Ramirez
  • 149
  • 1
  • 6