Mysql Node.js中的多个SQL查询

Mysql Node.js中的多个SQL查询,mysql,node.js,express,Mysql,Node.js,Express,我正在为用户注册制定路线,我需要使用多个查询,以便确定数据库中是否存在用户名和电子邮件 我的第一个解决方案是创建一个连接。query内部连接。query但它不起作用 const { username, email, password, confirm } = req.body var error_mssg = [] connection.query( `SELECT COUNT(*) as usernameCount FROM adminuser WHERE username="$

我正在为
用户注册制定
路线
,我需要使用
多个查询
,以便确定数据库中是否存在
用户名
电子邮件

我的第一个解决方案是创建一个
连接。query
内部
连接。query
但它不起作用

const { username, email, password, confirm } = req.body
var error_mssg = []
connection.query(
  `SELECT COUNT(*) as usernameCount FROM adminuser WHERE username="${username}"`,
  (err, data) => {
    if (err) throw err;

    if(data[0].usernameCount){
      error_mssg.push("username already exist")
    }

    connection.query(
      `SELECT COUNT(*) as emailCount FROM adminuser WHERE email="${email}"`,
      (err, data) => {
        if (err) throw err;

        if (data[0].emailCount > 0){
          error_mssg.push("email aready exist")
        }
  }
);
我还尝试在我的
mysql.createConnection
中添加
multiplestatems:true
,这很好,但我记得我仍然需要建立另一个
连接。如果在数据库中找不到相同的
username/email
,如果
password==confirm
,则查询

connection.query(
  `SELECT COUNT(*) as usernameCount FROM adminuser WHERE username="${username}";
  SELECT COUNT(*) as emailCount FROM adminuser WHERE email="${email}"`,
  (err, data) => {
    if (err) throw err;

    if(data[0][0].usernameCount){
      error_mssg.push("username already exist")
    }

    if(data[1][0].emailCount){
      error_mssg.push("email already exist")
    }

    if(password === confirm){
      if(error_mssg.lenght > 0){
        *******query to insert user information*******
      }else{
        res.send(error_mssg)
      }

    }else{
      error_mssg.push("password does not match")
      res.send(error_mssg)
    }
  }
);

如果
error\u mssg
为空,如何使用
多个查询
,然后插入数据。

您可以尝试使用
异步
查询。在同一个查询中尝试使用多个语句时,最好使用相同的方法

const usernameCount = await db.query( `SELECT COUNT(*) as usernameCount FROM adminuser WHERE username="${username}"` );
const emailCount = await db.query( `SELECT COUNT(*) as emailCount FROM adminuser WHERE email="${email}"` );

您可以尝试使用
async
查询。在同一个查询中尝试使用多个语句时,最好使用相同的方法

const usernameCount = await db.query( `SELECT COUNT(*) as usernameCount FROM adminuser WHERE username="${username}"` );
const emailCount = await db.query( `SELECT COUNT(*) as emailCount FROM adminuser WHERE email="${email}"` );
注射 首先,关闭注入漏洞。用户输入不应被信任,也不应直接传递给另一个子系统。最好的方法是在侧通道中传递用户数据;在SQL中,这意味着使用准备好的查询。例如,您可以使用
connection.execute
,而不是
connection.query

connection.execute(
    'SELECT Count(*) AS usernameCount FROM adminuser WHERE username=?',
    [username],
    (err, data) => {...}
)

可用的MySQL驱动程序可能只支持逃逸参数和模拟准备的语句,但这比没有什么好(尽管如果是这样的话,您应该认真考虑切换到支持准备语句的一个):

逻辑错误 仔细查看多语句示例中的块:

if(password === confirm){
  if(error_mssg.lenght > 0){
    *******query to insert user information*******
  }else{
    res.send(error_mssg)
  }
...
请注意,如果有错误消息,则插入用户信息,如果没有,则返回(空)错误消息列表。这与你想要的正好相反。调试器可以帮助捕获这种逻辑错误

您可以发送所有错误消息,而不是仅在密码不匹配时发送存在错误消息。通过这种方式,用户可以一次更正所有错误,而不必仅提交以发现其他错误。这也将更好地将请求验证与请求处理分开

if (password !== confirm) {
    error_msgs.push("passwords do not match");
}

if (error_msgs.length) {
    res.send(error_msgs);
} else {
    // create new user
    ...
}
存在性检验 查询现有记录的另一种方法是使用单个查询来检查
adminuser
数据库中的现有用户名和电子邮件地址。与链式查询或
wait
s相比,这具有效率优势,因为一个查询不需要等待另一个查询完成(尽管后两个查询的性能成本可能可以忽略不计)

由于这两个字段是同一数据库中的独立列,因此查询非常简单:

SELECT username, email FROM adminuser WHERE username=? OR email=?;
应通过使用
unique
索引在模式中反映用户名和电子邮件应唯一的数据要求。这将限制结果行最多为2行(每个值的最大出现次数为1),然后可以检查是否存在唯一值

connection.execute(
    'SELECT username, email FROM adminuser WHERE username=? OR email=?;',
    [username, email],
    (err, data) => {
        if (err) throw err;
        
        /*** Validate user creation request ***/
        /* There should be at most 2 rows, with at most 1 occurrence each of `username` and `email`. */
        for (var iRow=0; iRow < data.length; ++iRow) {
            if (username == data[iRow].username) {
                error_msgs.push("username already exists");
            }
            if (email == data[iRow].email) {
                error_msgs.push("email already exists");
            }
        }
        
        if (password !== confirm) {
            error_msgs.push("passwords do not match");
        }
        
        /*** Handle user creation request ***/
        if (error_msgs.length) {
            res.send(error_msgs);
        } else {
            // create new user
            ...
        }
    }
);
其他设计注意事项 示例代码混合了两个不同的关注点:数据库访问和请求处理(validation&c.)。会减少。一个模块(数据访问层)应处理数据库中用户的管理(例如,检查现有记录中的用户名或电子邮件,添加新用户),另一个模块应通过向DAL发出适当的调用(可能是间接的)来处理用户创建请求(验证和用户创建)。特别是,SQL语句和数据库调用不应出现在请求处理程序中。请求处理程序可能类似于:

async function validateUserCreationRequest(req) {
    const { username, email, password, confirm } = req.body;
    var error_msgs = [],
        userExists = await User.exists({ username, email }); // here, User is responsible for accessing the DAL

    if (userExists) {
        if (userExists.usernameCount) {
            error_msgs.push("username already exists");
        }
        if (userExists.emailCount) {
            error_msgs.push("email already exists");
        }
    }
    
    if (password !== confirm) {
        error_msgs.push("passwords do not match");
    }
    
    if (error_msgs.length) {
        return error_msgs;
    }
    return null;
}

function createUser(req) {
    return User.create(req); // again, User is responsible for accessing the DAL
}

function handleUserCreationRequest(req) {
    var errors = validateUserCreationRequest(req);
    if (errors) {
        res.send(errors);
    } else {
        createUser(req);
    }
}
注射 首先,关闭注入漏洞。用户输入不应被信任,也不应直接传递给另一个子系统。最好的方法是在侧通道中传递用户数据;在SQL中,这意味着使用准备好的查询。例如,您可以使用
connection.execute
,而不是
connection.query

connection.execute(
    'SELECT Count(*) AS usernameCount FROM adminuser WHERE username=?',
    [username],
    (err, data) => {...}
)

可用的MySQL驱动程序可能只支持逃逸参数和模拟准备的语句,但这比没有什么好(尽管如果是这样的话,您应该认真考虑切换到支持准备语句的一个):

逻辑错误 仔细查看多语句示例中的块:

if(password === confirm){
  if(error_mssg.lenght > 0){
    *******query to insert user information*******
  }else{
    res.send(error_mssg)
  }
...
请注意,如果有错误消息,则插入用户信息,如果没有,则返回(空)错误消息列表。这与你想要的正好相反。调试器可以帮助捕获这种逻辑错误

您可以发送所有错误消息,而不是仅在密码不匹配时发送存在错误消息。通过这种方式,用户可以一次更正所有错误,而不必仅提交以发现其他错误。这也将更好地将请求验证与请求处理分开

if (password !== confirm) {
    error_msgs.push("passwords do not match");
}

if (error_msgs.length) {
    res.send(error_msgs);
} else {
    // create new user
    ...
}
存在性检验 查询现有记录的另一种方法是使用单个查询来检查
adminuser
数据库中的现有用户名和电子邮件地址。与链式查询或
wait
s相比,这具有效率优势,因为一个查询不需要等待另一个查询完成(尽管后两个查询的性能成本可能可以忽略不计)

由于这两个字段是同一数据库中的独立列,因此查询非常简单:

SELECT username, email FROM adminuser WHERE username=? OR email=?;
应通过使用
unique
索引在模式中反映用户名和电子邮件应唯一的数据要求。这将限制结果行最多为2行(每个值的最大出现次数为1),然后可以检查是否存在唯一值

connection.execute(
    'SELECT username, email FROM adminuser WHERE username=? OR email=?;',
    [username, email],
    (err, data) => {
        if (err) throw err;
        
        /*** Validate user creation request ***/
        /* There should be at most 2 rows, with at most 1 occurrence each of `username` and `email`. */
        for (var iRow=0; iRow < data.length; ++iRow) {
            if (username == data[iRow].username) {
                error_msgs.push("username already exists");
            }
            if (email == data[iRow].email) {
                error_msgs.push("email already exists");
            }
        }
        
        if (password !== confirm) {
            error_msgs.push("passwords do not match");
        }
        
        /*** Handle user creation request ***/
        if (error_msgs.length) {
            res.send(error_msgs);
        } else {
            // create new user
            ...
        }
    }
);
其他设计注意事项 示例代码混合了两个不同的关注点:数据库访问和请求处理(validation&c.)。会减少。一个模块,数据访问层,应该处理管理databa中的用户