如何在 Postgresql for C++ 中准备语句和绑定参数

How to prepare statements and bind parameters in Postgresql for C++

我是 C++ 的新手,对 pqxx 库略知一二。我要实现的是准备语句和绑定参数。在 PHP 中,我习惯于以如此简洁明了的方式进行此操作:

$s = $db->prepare("SELECT id FROM mytable WHERE id = :id");
$s->bindParam(':id', $id);
$s->execute();

或使用令牌:

$data = array();
$data[] = 1;
$data[] = 2;
$s = $db->prepare("SELECT id FROM mytable WHERE id = ? or id = ?");
$s->execute($data);

我试图从 pqxx documentation 中弄清楚如何实现它,但对我来说文档看起来一团糟并且缺乏简短的示例(就像我在上面提供的那样)。我希望有人在处理 C++.

中的 Postgresql 时也能提供这样简单的示例(或相当简单 - 无需编写一些庞然大物的代码)

一个简单的例子。这仅打印 id 值为 0 的条目数。

#include<pqxx/pqxx>
#include<iostream>

int main()
{
    std::string name = "name";
    int id = 0;
    try {
        //established connection to data base
        pqxx::connection c("dbname=mydb user=keutoi");
        pqxx::work w(c);
        //statement template
        c.prepare("example", "SELECT id  FROM mytable WHERE id = ");
        //invocation as in varible binding
        pqxx::result r = w.prepared("example")(id).exec();
        
        w.commit();
        //result handling for accessing arrays and conversions look at docs
        std::cout << r.size() << std::endl;
    }
    catch(const std::exception &e)
    {
        std::cerr << e.what() << std::endl;
        return 1;
    }
    return 0;
}

函数w.prepared()有点复杂。它类似于 haskell 中的 curried(curry) 函数,因为它接受一个参数,而 returns 另一个函数又接受另一个参数。那种东西。

文档说:

How do you pass those parameters? C++ has no good way to let you pass an unlimited, variable number of arguments to a function call, and the compiler does not know how many you are going to pass. There's a trick for that: you can treat the value you get back from prepared as a function, which you call to pass a parameter. What you get back from that call is the same again, so you can call it again to pass another parameter and so on.

Once you've passed all parameters in this way, you invoke the statement with the parameters by calling exec on the invocation

如果有更多的参数在prepare函数中使用$1 $2等等。

c.prepare("SELECT id name FROM mytable WHERE id =  AND name = ")

并将变量指定为

w.prepared("example")(dollar1_var)(dollar2_var).exec()

动态准备示例

#include<pqxx/pqxx>
#include<iostream>
#include<vector>

//Just give a vector of data you can change the template<int> to any data type
pqxx::prepare::invocation& prep_dynamic(std::vector<int> data, pqxx::prepare::invocation& inv)
{
    for(auto data_val : data)
        inv(data_val);
    return inv;
}

int main()
{
    std::string name = "name";

    //a data array to be used.
    std::vector<int> ids;
    ids.push_back(0);
    ids.push_back(1);

    try {
        pqxx::connection c("dbname=mydb user=keutoi");
        pqxx::work w(c);

        c.prepare("example", "SELECT id  FROM mytable WHERE id =  or id = ");
        pqxx::prepare::invocation w_invocation = w.prepared("example");

        //dynamic array preparation
        prep_dynamic(ids, w_invocation);
        //executing prepared invocation.
        pqxx::result r = w_invocation.exec();

        w.commit();

        std::cout << r.size() << std::endl;
    }
    catch(const std::exception &e)
    {
        std::cerr << e.what() << std::endl;
        return 1;
    }
    return 0;
}

如果你想处理其他数据类型使用这个函数定义

template<class T> pqxx::prepare::invocation& prep_dynamic(std::vector<T> data, pqxx::prepare::invocation& inv)
{
    for(auto data_val : data)
        inv(data_val);
    return inv;
}

尽可能使用pqxx::prepare::invocation,并在执行前绑定更多的值,因为它更稳定并且可以预防错误,但还有一种更快的方法,正如我在下面描述的那样。

我。 随着调用:

pqxx::nontransaction W(C);
std::string m_insertCommand = "INSERT INTO tableforperftest(column1, column2) VALUES";


unsigned  int m_nCurrentRow = 32767;

for (size_t i = 0; i < m_nCurrentRow; i++)
{
    unsigned int countOf$ = i * 2;
    for (unsigned int i = 0; i < 2; ++i)
    {
        if (i == 0)
        {
            m_insertCommand += "(";
        }
        else
        {
            m_insertCommand += ", ";
        }
        m_insertCommand += "$";
        std::stringstream ss;
        ss << countOf$ + i + 1;
        m_insertCommand += ss.str();
    }
   if(i < m_nCurrentRow - 1)
    m_insertCommand += ") ,";
}
m_insertCommand += ")";

C.prepare("insert_into_db", m_insertCommand);
pqxx::prepare::invocation inv = W.prepared("insert_into_db");

for (size_t i = 0; i < m_nCurrentRow; i++)
{
    inv(i)(i);
}

inv.exec();

二. 使用获取更多参数值的存储过程:

CREATE OR REPLACE FUNCTION insertintoboosted(valuesforinsert TEXT) RETURNS VOID AS
$$ 
BEGIN
     EXECUTE 'INSERT INTO tableforperftestproof(column1, column2) VALUES (' || valuesforinsert || ')';
END;
$$
LANGUAGE plpgsql;

代码:

for (size_t i = 0; i < m_nCurrentRow; i++)
{

    if (i == 0)
        ss  << i << "," << i;
    else
        ss << "(" << i << "," << i;

    if (i < m_nCurrentRow - 1)
        ss << "),";
}

C.prepare("prep2", "select insertintoboosted(::text)");

W.prepared("prep2")(ss).exec();

三。 每次都有参数绑定和执行:

std::string m_insertCommand3 = "INSERT INTO tableforperftest(column1, column2) VALUES (, )";
C.prepare("insert_into_db3", m_insertCommand3);
for (size_t i = 0; i < m_nCurrentRow; i++)
{
    W.prepared("insert_into_db3")(i)(i).exec();
}

比较 32767 个插入的解决方案:

Invocation:                              --> Elapsed:    0.250292s
Stored Proc:                             --> Elapsed:    0.154507s 
Parameter binding + execution each time: --> Elapsed:    29.5566s