Mysql
 sql >> Baza danych >  >> RDS >> Mysql

Dodawanie wierszy do tabeli z unikalną kolumną:Uzyskaj istniejące wartości identyfikatorów oraz nowo utworzone

To pierwsze podejście nie zakłada żadnych założeń dotyczących zachowania sterownika JDBC podczas obsługi wsadowych operacji INSERT. Pozwala uniknąć potencjalnych błędów INSERT dzięki

  • wypytywanie tabeli o dowolny istniejący mail_id wartości w naszym aktualnym zestawie danych,
  • odnotowuje odpowiedni id wartość dla tych mail_id wartości, które istnieją,
  • WSTAWIA mail_id wartości, które nie istnieją, i pobiera ich (nowy) id wartości, a następnie
  • wstawia wiersze w drugiej tabeli (inv_table ).
try (Connection dbConn = DriverManager.getConnection(myConnectionString, "root", "usbw")) {
    dbConn.setAutoCommit(false);

    // test data and setup
    Long aid = 123L;
    List<MailidInvitation> invitationList = new ArrayList<MailidInvitation>();
    invitationList.add(new MailidInvitation(13L));
    invitationList.add(new MailidInvitation(11L));
    invitationList.add(new MailidInvitation(12L));
    // remove stuff from previous test run
    try (Statement s = dbConn.createStatement()) {
        s.executeUpdate("DELETE FROM mail_contacts WHERE mail_id IN (11,13)");
    }
    try (PreparedStatement ps = dbConn.prepareStatement(
            "DELETE FROM inv_table WHERE aid=?")) {
        ps.setLong(1, aid);
        ps.executeUpdate();
    }

    // real code starts here
    //
    // create a Map to hold `mail_id` and their corresponding `id` values 
    Map<Long, Long> mailIdMap = new TreeMap<Long, Long>();
    for (MailidInvitation a : invitationList) {
        // mail_id, id (id is null for now)
        mailIdMap.put(a.getId(), null);
    }

    // build an SQL statement to retrieve any existing values
    StringBuilder sb = new StringBuilder(
            "SELECT id, mail_id " +
            "FROM mail_contacts " +
            "WHERE mail_id IN (");
    int n = 0;
    for (Map.Entry<Long, Long> entry : mailIdMap.entrySet()) {
        if (n++ > 0) sb.append(',');
        sb.append(entry.getKey());
    }
    sb.append(')');
    String sql = sb.toString();

    // run the query and save the results (if any) to the Map
    try (Statement s = dbConn.createStatement()) {
        // <demo>
        System.out.println(sql);
        // </demo>
        try (ResultSet rs = s.executeQuery(sql)) {
            while (rs.next()) {
                mailIdMap.put(rs.getLong("mail_id"), rs.getLong("id"));
            }
        }
    }

    // <demo>
    System.out.println();
    System.out.println("mailIdMap now contains:");
    // </demo>

    // build a list of the `mail_id` values to INSERT (where id == null)
    //     ... and print the existing mailIdMap values for demo purposes
    List<Long> mailIdsToInsert = new ArrayList<Long>();
    for (Map.Entry<Long, Long> entry : mailIdMap.entrySet()) {
        String idValue = "";  // <demo />
        if (entry.getValue() == null) {
            mailIdsToInsert.add(entry.getKey());
            // <demo>
            idValue = "null";  
        } else {
            idValue = entry.getValue().toString();
            // </demo>
        }
        // <demo>
        System.out.println(String.format(
                "    %d - %s", 
                entry.getKey(),
                idValue));
        // </demo>
    }

    // batch insert `mail_id` values that don't already exist
    try (PreparedStatement ps = dbConn.prepareStatement(
            "INSERT INTO mail_contacts (mail_id) VALUES (?)", 
            PreparedStatement.RETURN_GENERATED_KEYS)) {
        for (Long mid : mailIdsToInsert) {
            ps.setLong(1, mid);
            ps.addBatch();
        }
        ps.executeBatch();
        // get generated keys and insert them into the Map
        try (ResultSet rs = ps.getGeneratedKeys()) {
            n = 0;
            while (rs.next()) {
                mailIdMap.put(mailIdsToInsert.get(n++), rs.getLong(1));
            }
        }
    }

    // <demo>
    System.out.println();
    System.out.println("After INSERT INTO mail_contacts, mailIdMap now contains:");
    for (Map.Entry<Long, Long> entry : mailIdMap.entrySet()) {
        System.out.println(String.format(
                "    %d - %s", 
                entry.getKey(),
                entry.getValue()));
    }
    // </demo>

    // now insert the `inv_table` rows
    try (PreparedStatement ps = dbConn.prepareStatement(
            "INSERT INTO inv_table (mid, aid) VALUES (?,?)")) {
        ps.setLong(2, aid);
        for (MailidInvitation a : invitationList) {
            ps.setLong(1, mailIdMap.get(a.getId()));
            ps.addBatch();
        }
        ps.executeBatch();
    }
    dbConn.commit();
}

Wynik konsoli wygląda tak:

SELECT id, mail_id FROM mail_contacts WHERE mail_id IN (11,12,13)

mailIdMap now contains:
    11 - null
    12 - 1
    13 - null

After INSERT INTO mail_contacts, mailIdMap now contains:
    11 - 15
    12 - 1
    13 - 16

Niektóre sterowniki JDBC umożliwiają kontynuację partii, jeśli jedna lub więcej instrukcji w partii nie powiedzie się. Na przykład w MySQL Connector/J opcja to continueBatchOnError co jest true domyślnie. W takich przypadkach alternatywnym podejściem byłaby próba WSTAWIENIA całego mail_id wartości i sprawdź liczniki aktualizacji zwrócone przez Batch. Pomyślne WSTAWKI zwróciłyby UpdateCount równy 1, podczas gdy WSTAWKI zakończone niepowodzeniem z powodu istniejącego mail_id zwróci EXECUTE_FAILED (-3). Następnie moglibyśmy pobrać (nowy) id wartości dla udanych WSTAWEK za pośrednictwem .getGeneratedKeys() , a następnie przystąp do tworzenia instrukcji SELECT, aby wrócić i pobrać id wartości dla mail_id wpisy, które już istniały.

Więc kod taki jak ten

// create a Map to hold `mail_id` and their corresponding `id` values 
Map<Long, Long> mailIdMap = new TreeMap<Long, Long>();
for (MailidInvitation a : invitationList) {
    // mail_id, id (id is null for now)
    mailIdMap.put(a.getId(), null);
}

// try INSERTing all `mail_id` values
try (PreparedStatement ps = dbConn.prepareStatement(
        "INSERT INTO mail_contacts (mail_id) VALUES (?)", 
        PreparedStatement.RETURN_GENERATED_KEYS)) {
    for (Long mid : mailIdMap.keySet()) {
        ps.setLong(1, mid);
        ps.addBatch();
    }
    int[] updateCounts = null;
    try {
        updateCounts = ps.executeBatch();
    } catch (BatchUpdateException bue) {
        updateCounts = bue.getUpdateCounts();
    }
    // get generated keys and insert them into the Map
    try (ResultSet rs = ps.getGeneratedKeys()) {
        int i = 0;
        for (Long mid : mailIdMap.keySet()) {
            if (updateCounts[i++] == 1) {
                rs.next();
                mailIdMap.put(mid, rs.getLong(1));
            }
        }
    }
}

// <demo>
System.out.println("mailIdMap now contains:");
// </demo>

// build a SELECT statement to get the `id` values for `mail_id`s that already existed
//     ... and print the existing mailIdMap values for demo purposes
StringBuilder sb = new StringBuilder(
        "SELECT id, mail_id " +
        "FROM mail_contacts " +
        "WHERE mail_id IN (");
int n = 0;
for (Map.Entry<Long, Long> entry : mailIdMap.entrySet()) {
    String idValue = "";  // <demo />
    if (entry.getValue() == null) {
        if (n++ > 0) sb.append(',');
        sb.append(entry.getKey());
        // <demo>
        idValue = "null";  
    } else {
        idValue = entry.getValue().toString();
        // </demo>
    }
    // <demo>
    System.out.println(String.format(
            "    %d - %s", 
            entry.getKey(),
            idValue));
    // </demo>
}
sb.append(')');
String sql = sb.toString();

// <demo>
System.out.println();
System.out.println(sql);
// </demo>

wygeneruje dane wyjściowe konsoli w następujący sposób:

mailIdMap now contains:
    11 - 17
    12 - null
    13 - 19

SELECT id, mail_id FROM mail_contacts WHERE mail_id IN (12)

Reszta procesu byłaby taka sama jak poprzednio:

  • wypełnij pozostałe mailIdMap wpisy i
  • przetwórz INSERT w drugiej tabeli przy użyciu id wartości w mailIdMap .



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Przechowywanie ciągu binarnego w MySQL

  2. MySQL średnia liczba godzin między utworzonymi datami i godzinami w określonym przedziale czasu

  3. Puste pliki wygenerowane podczas uruchamiania programu `mysqldump` przy użyciu PHP

  4. Funkcja kanoniczna EntityFunctions.TruncateTime nie istnieje w MYSQL

  5. Dodaj dane do istniejących danych w bazie danych MySQL