从DELETE和INSERT切换到UPDATE并且速度更快

时间:2015-07-13 20:14:45

标签: java mysql jdbc

public boolean saveHighScore(Client c) throws SQLException {
    long totallvlexp = (long) (c.playerXP[0]) + (c.playerXP[1]) + (c.playerXP[2]) + (c.playerXP[3]) + (c.playerXP[4]) + (c.playerXP[5]) + (c.playerXP[6]) + (c.playerXP[7]) + (c.playerXP[8]) + (c.playerXP[9]) + (c.playerXP[10]) + (c.playerXP[11]) + (c.playerXP[12]) + (c.playerXP[13]) + (c.playerXP[14]) + (c.playerXP[15]) + (c.playerXP[16]) + (c.playerXP[17]) + (c.playerXP[18]) + (c.playerXP[19]) + (c.playerXP[20]); 
    int totallevell = (int) (c.getLevelForXP(c.playerXP[0]) + c.getLevelForXP(c.playerXP[1]) + c.getLevelForXP(c.playerXP[2]) + c.getLevelForXP(c.playerXP[3]) + c.getLevelForXP(c.playerXP[4]) + c.getLevelForXP(c.playerXP[5]) + c.getLevelForXP(c.playerXP[6]) + c.getLevelForXP(c.playerXP[7]) + c.getLevelForXP(c.playerXP[8]) + c.getLevelForXP(c.playerXP[9]) + c.getLevelForXP(c.playerXP[10]) + c.getLevelForXP(c.playerXP[11]) + c.getLevelForXP(c.playerXP[12]) + c.getLevelForXP(c.playerXP[13]) + c.getLevelForXP(c.playerXP[14]) + c.getLevelForXP(c.playerXP[15]) + c.getLevelForXP(c.playerXP[16]) + c.getLevelForXP(c.playerXP[17]) + c.getLevelForXP(c.playerXP[18]) + c.getLevelForXP(c.playerXP[19]) + c.getLevelForXP(c.playerXP[20]));
    String delQuery1 = "DELETE FROM `skills` WHERE playerName = '"+c.playerName+"';";
    String delQuery2 = "DELETE FROM `skillsoverall` WHERE playerName = '"+c.playerName+"';";
    String delQuery3 = "DELETE FROM `playerrights` WHERE playerName = '"+c.playerName+"';";
    String insQuery1 = "INSERT INTO `skills` (`playerName`,`Attacklvl`,`Attackxp`,`Defencelvl`,`Defencexp`,`Strengthlvl`,`Strengthxp`,`Hitpointslvl`,`Hitpointsxp`,`Rangelvl`,`Rangexp`,`Prayerlvl`,`Prayerxp`,`Magiclvl`,`Magicxp`,`Cookinglvl`,`Cookingxp`,`Woodcuttinglvl`,`Woodcuttingxp`,`Fletchinglvl`,`Fletchingxp`,`Fishinglvl`,`Fishingxp`,`Firemakinglvl`,`Firemakingxp`,`Craftinglvl`,`Craftingxp`,`Smithinglvl`,`Smithingxp`,`Mininglvl`,`Miningxp`,`Herblorelvl`,`Herblorexp`,`Agilitylvl`,`Agilityxp`,`Thievinglvl`,`Thievingxp`,`Slayerlvl`,`Slayerxp`,`Farminglvl`,`Farmingxp`,`Runecraftlvl`,`Runecraftxp`) VALUES ('"+c.playerName+"',"+c.playerLevel[0]+","+c.playerXP[0]+","+c.playerLevel[1]+","+c.playerXP[1]+","+c.playerLevel[2]+","+c.playerXP[2]+","+c.playerLevel[3]+","+c.playerXP[3]+","+c.playerLevel[4]+","+c.playerXP[4]+","+c.playerLevel[5]+","+c.playerXP[5]+","+c.playerLevel[6]+","+c.playerXP[6]+","+c.playerLevel[7]+","+c.playerXP[7]+","+c.playerLevel[8]+","+c.playerXP[8]+","+c.playerLevel[9]+","+c.playerXP[9]+","+c.playerLevel[10]+","+c.playerXP[10]+","+c.playerLevel[11]+","+c.playerXP[11]+","+c.playerLevel[12]+","+c.playerXP[12]+","+c.playerLevel[13]+","+c.playerXP[13]+","+c.playerLevel[14]+","+c.playerXP[14]+","+c.playerLevel[15]+","+c.playerXP[15]+","+c.playerLevel[16]+","+c.playerXP[16]+","+c.playerLevel[17]+","+c.playerXP[17]+","+c.playerLevel[18]+","+c.playerXP[18]+","+c.playerLevel[19]+","+c.playerXP[19]+","+c.playerLevel[20]+","+c.playerXP[20]+");";
    String insQuery2 = "INSERT INTO `skillsoverall` (`playerName`,`lvl`,`xp`) VALUES ('"+c.playerName+"',"+totallevell+","+totallvlexp+");";
    String insQuery3 = "INSERT INTO `playerrights` (`playerName`,`rank`) VALUES ('"+c.playerName+"',"+c.playerRights+");";
    String[] delQueries = {delQuery1, delQuery2, delQuery3};
    String[] insQueries = {insQuery1, insQuery2, insQuery3};    

    try {
        Class.forName("com.mysql.jdbc.Driver").newInstance();
        try(Connection conX = DriverManager.getConnection("jdbc:mysql://localhost/highscores","root","root")) {
            try(Statement stmtX = conX.createStatement()) {
                for(int i = 0; i < delQueries.length; i++)
                {
                    stmtX.executeUpdate(delQueries[i]);
                    stmtX.executeUpdate(insQueries[i]);
                }

            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    } catch (Exception e) {
        e.printStackTrace();
        return false;
    }
    return true;
}       

}

使用此代码我怎样才能让它更快?如果我使用UPDATE而不是它会更快?如果它会请我告诉我如何使用我提供的代码。我对SQL不太满意,只是帮助了一位朋友。

2 个答案:

答案 0 :(得分:2)

改善这种情况的一些方法:

  • 使用UPDATE语句并使用WHERE子句中主键的值。这是为了你知道id。如果你不知道,那么
  • 正确使用UPDATE语句。在将用于WHERE子句的列上添加适当的索引,如果是单个列且它不是主键,则此语句可能会多次执行。
  • 更新相关列,而不是所有列(除非您要更新该行的所有值,这是奇怪的但不是不可能的。)
  • 由于您希望/需要更新所有内容或不需要更新,因此您应该使用事务。您可以使用Connection#setAutoCommit(false);开始交易,然后使用Connection#commit关闭它。如果出现错误,请使用Connection#rollback(),并且所有操作都不会影响数据库。
  • 如此天真地停止使用Statement 。如果您的查询需要参数化,请改用PreparedStatement

在您的数据库中,执行以下操作:

ALTER TABLE skills ADD INDEX idx_playerName (playerName);

以下是使用上述语句更新的Java代码的一部分:

public boolean saveHighScore(Client c) throws SQLException {
    long totallvlexp = (long) (c.playerXP[0]) + (c.playerXP[1]) + (c.playerXP[2]) + (c.playerXP[3]) + (c.playerXP[4]) + (c.playerXP[5]) + (c.playerXP[6]) + (c.playerXP[7]) + (c.playerXP[8]) + (c.playerXP[9]) + (c.playerXP[10]) + (c.playerXP[11]) + (c.playerXP[12]) + (c.playerXP[13]) + (c.playerXP[14]) + (c.playerXP[15]) + (c.playerXP[16]) + (c.playerXP[17]) + (c.playerXP[18]) + (c.playerXP[19]) + (c.playerXP[20]); 
    int totallevell = (int) (c.getLevelForXP(c.playerXP[0]) + c.getLevelForXP(c.playerXP[1]) + c.getLevelForXP(c.playerXP[2]) + c.getLevelForXP(c.playerXP[3]) + c.getLevelForXP(c.playerXP[4]) + c.getLevelForXP(c.playerXP[5]) + c.getLevelForXP(c.playerXP[6]) + c.getLevelForXP(c.playerXP[7]) + c.getLevelForXP(c.playerXP[8]) + c.getLevelForXP(c.playerXP[9]) + c.getLevelForXP(c.playerXP[10]) + c.getLevelForXP(c.playerXP[11]) + c.getLevelForXP(c.playerXP[12]) + c.getLevelForXP(c.playerXP[13]) + c.getLevelForXP(c.playerXP[14]) + c.getLevelForXP(c.playerXP[15]) + c.getLevelForXP(c.playerXP[16]) + c.getLevelForXP(c.playerXP[17]) + c.getLevelForXP(c.playerXP[18]) + c.getLevelForXP(c.playerXP[19]) + c.getLevelForXP(c.playerXP[20]));
    boolean result = true;
    String updateSkillsSql = "UPDATE skills"
        + " SET `Attacklvl`=?,"
        + " `Attackxp`=?,"
        + " `Defencelvl`=?,"
        + " `Defencexp`=?,"
        + " `Strengthlvl`=?,"
        + " `Strengthxp`=?,"
        + " `Hitpointslvl`=?,"
        + " `Hitpointsxp`=?,"
        + " `Rangelvl`=?,"
        + " `Rangexp`=?,"
        + " `Prayerlvl`=?,"
        + " `Prayerxp`=?,"
        + " `Magiclvl`=?,"
        + " `Magicxp`=?,"
        + " `Cookinglvl`=?,"
        + " `Cookingxp`=?,"
        + " `Woodcuttinglvl`=?,"
        + " `Woodcuttingxp`=?,"
        + " `Fletchinglvl`=?,"
        + " `Fletchingxp`=?,"
        + " `Fishinglvl`=?,"
        + " `Fishingxp`=?,"
        + " `Firemakinglvl`=?,"
        + " `Firemakingxp`=?,"
        + " `Craftinglvl`=?,"
        + " `Craftingxp`=?,"
        + " `Smithinglvl`=?,"
        + " `Smithingxp`=?,"
        + " `Mininglvl`=?,"
        + " `Miningxp`=?,"
        + " `Herblorelvl`=?,"
        + " `Herblorexp`=?,"
        + " `Agilitylvl`=?,"
        + " `Agilityxp`=?,"
        + " `Thievinglvl`=?,"
        + " `Thievingxp`=?,"
        + " `Slayerlvl`=?,"
        + " `Slayerxp`=?,"
        + " `Farminglvl`=?,"
        + " `Farmingxp`=?,"
        + " `Runecraftlvl`=?,"
        + " `Runecraftxp`=?"
        + " WHERE playerName = ?";
    //do similar for the other queries...

    //not needed since JDBC 4, noted since you're using Java 7
    //Class.forName("com.mysql.jdbc.Driver").newInstance();
    try(Connection con = DriverManager.getConnection("jdbc:mysql://localhost/highscores","root","root")) {
        //when storing multiple data, it's better to use a transaction
        con.setAutoCommit(false);
        try(PreparedStatement pstmt = con.prepareStatement(updateSkillsSql);
            //declare the other PreparedStatements for each update sql statement here...
            ) {
            //do something like this for every PreparedStatement
            setParameters(pstmt,
                c.playerLevel[0], c.playerXP[0],
                c.playerLevel[1], c.playerXP[1],
                c.playerLevel[2], c.playerXP[2],
                c.playerLevel[3], c.playerXP[3],
                c.playerLevel[4], c.playerXP[4],
                c.playerLevel[5], c.playerXP[5],
                c.playerLevel[6], c.playerXP[6],
                c.playerLevel[7], c.playerXP[7],
                c.playerLevel[8], c.playerXP[8],
                c.playerLevel[9], c.playerXP[9],
                c.playerLevel[10], c.playerXP[10],
                c.playerLevel[11], c.playerXP[11],
                c.playerLevel[12], c.playerXP[12],
                c.playerLevel[13], c.playerXP[13],
                c.playerLevel[14], c.playerXP[14],
                c.playerLevel[15], c.playerXP[15],
                c.playerLevel[16], c.playerXP[16],
                c.playerLevel[17], c.playerXP[17],
                c.playerLevel[18], c.playerXP[18],
                c.playerLevel[19], c.playerXP[19],
                c.playerLevel[20], c.playerXP[20],
                c.playerName);
            pstmt.executeUpdate();
        } catch (Exception e) {
            System.out.println(String.format("There's a problem when saving the data of  player %s.", c.playerName));
            e.printStackTrace(System.out);
            con.rollback();
            result = false;
        }
        if (result) {
            con.commit();
            con.setAutoCommit(true);
        }
    } catch (Exception e) {
        System.out.println(String.format("There's a problem when saving the data of  player %s.", c.playerName));
        e.printStackTrace(System.out);
        result = false;
    }
    return result;
}

//created method to add parameters despite its type    
private void setParameters(PreparedStatement pstmt, Object ... args) {
    int i = 1;
    for (Object arg : args) {
        pstmt.setObject(i++, arg);
    }
}

答案 1 :(得分:0)

DELETE / INSERT方法或UPDATE方法中的最大费用可能是您的WHERE条款。

执行UPDATEDELETEWHERE指向playerName = 'FriendlyNameOfPlayer'是残酷的。如果在该字段上创建索引,则可以使其更好一些。

您可以使用......的某些内容创建索引。

CREATE INDEX playerName_ix ON skills (playerName)
CREATE INDEX playerName_ix ON skillsoverall (playerName)

详细了解可能与具体情况相关的其他一些选项here

更好的做法是按行PK(可能是自动递增的整数?)来定位行。理想情况下,这背后的架构将是一个名为Player_Master的表,其自动递增整数为PK,然后是另一列中存储的播放器的友好名称。像代码snippit中引用的那些表应该存储来自Player_Master的PK值(而不是友好名称),这将是FK&#39; s。您的DELETE语句看起来就像......

DELETE FROM `skills` WHERE skills.playerID = 37

重做架构将是&#34;正确的&#34;这样做的方法,但我得知每个项目可能没有完全拆解/重做所需的时间或资源。如果你的好友说'#34;你能让这个更快吗?&#34;,你可以做索引并走开。如果问题是&#34;我该如何做到这一点?&#34;,我会在计费时间开始计时,因为从提供的代码和架构的小瞥看,它不是快速解决。

我还会因为调用拼凑在一起的TSQL语句的原始执行而嘲笑你。有更好的方法来做到这一点。