SQLite add Primary Key

前端 未结 11 2407
你的背包
你的背包 2020-11-28 06:07

I created a table in Sqlite by using the CREATE TABLE AS syntax to create a table based on a SELECT statement. Now this table has no primary key b

相关标签:
11条回答
  • 2020-11-28 06:35

    I think adding an index on that column can get pretty much the same effect.

    0 讨论(0)
  • 2020-11-28 06:37

    Introduction

    This is based on Android's java and it's a good example on changing the database without annoying your application fans/customers. This is based on the idea of the SQLite FAQ page http://sqlite.org/faq.html#q11

    The problem

    I did not notice that I need to set a row_number or record_id to delete a single purchased item in a receipt, and at same time the item barcode number fooled me into thinking of making it as the key to delete that item. I am saving a receipt details in the table receipt_barcode. Leaving it without a record_id can mean deleting all records of the same item in a receipt if I used the item barcode as the key.

    Notice

    Please understand that this is a copy-paste of my code I am work on at the time of this writing. Use it only as an example, copy-pasting randomly won't help you. Modify this first to your needs

    Also please don't forget to read the comments in the code .

    The Code

    Use this as a method in your class to check 1st whether the column you want to add is missing . We do this just to not repeat the process of altering the table receipt_barcode. Just mention it as part of your class. In the next step you'll see how we'll use it.

    public boolean is_column_exists(SQLiteDatabase mDatabase , String table_name,
    String     column_name) {
        //checks if table_name has column_name
        Cursor cursor = mDatabase.rawQuery("pragma table_info("+table_name+")",null);
        while (cursor.moveToNext()){
        if (cursor.getString(cursor.getColumnIndex("name")).equalsIgnoreCase(column_name)) return true;
        }
        return false;
    }
    

    Then , the following code is used to create the table receipt_barcode if it already does NOT exit for the 1st time users of your app. And please notice the "IF NOT EXISTS" in the code. It has importance.

    //mDatabase should be defined as a Class member (global variable) 
    //for ease of access : 
    //SQLiteDatabse mDatabase=SQLiteDatabase.openOrCreateDatabase(dbfile_path, null);
    creation_query = " CREATE TABLE if not exists receipt_barcode ( ";
    creation_query += "\n record_id        INTEGER PRIMARY KEY AUTOINCREMENT,";
    creation_query += "\n rcpt_id INT( 11 )       NOT NULL,";
    creation_query += "\n barcode VARCHAR( 255 )  NOT NULL ,";
    creation_query += "\n barcode_price VARCHAR( 255 )  DEFAULT (0),";
    creation_query += "\n PRIMARY KEY ( record_id ) );";
    mDatabase.execSQL(creation_query);
    
    //This is where the important part comes in regarding the question in this page:
    
    //adding the missing primary key record_id in table receipt_barcode for older versions
            if (!is_column_exists(mDatabase, "receipt_barcode","record_id")){
                mDatabase.beginTransaction();
                try{
                    Log.e("record_id", "creating");
    
    
                     creation_query="CREATE TEMPORARY TABLE t1_backup(";
                     creation_query+="record_id INTEGER        PRIMARY KEY AUTOINCREMENT,";
                     creation_query+="rcpt_id INT( 11 )       NOT NULL,";
                     creation_query+="barcode VARCHAR( 255 )  NOT NULL ,";
                     creation_query+="barcode_price VARCHAR( 255 )  NOT NULL DEFAULT (0) );";
                     mDatabase.execSQL(creation_query);
    
                     creation_query="INSERT INTO t1_backup(rcpt_id,barcode,barcode_price) SELECT rcpt_id,barcode,barcode_price  FROM receipt_barcode;";
                     mDatabase.execSQL(creation_query);
    
                     creation_query="DROP TABLE receipt_barcode;";
                     mDatabase.execSQL(creation_query);
    
                     creation_query="CREATE TABLE receipt_barcode (";
                     creation_query+="record_id INTEGER        PRIMARY KEY AUTOINCREMENT,";
                     creation_query+="rcpt_id INT( 11 )       NOT NULL,";
                     creation_query+="barcode VARCHAR( 255 )  NOT NULL ,";
                     creation_query+="barcode_price VARCHAR( 255 )  NOT NULL DEFAULT (0) );";
                     mDatabase.execSQL(creation_query);
    
                     creation_query="INSERT INTO receipt_barcode(record_id,rcpt_id,barcode,barcode_price) SELECT record_id,rcpt_id,barcode,barcode_price  FROM t1_backup;";
                     mDatabase.execSQL(creation_query);
    
                     creation_query="DROP TABLE t1_backup;";
                     mDatabase.execSQL(creation_query);
    
    
                     mdb.setTransactionSuccessful();
                } catch (Exception exception ){
                    Log.e("table receipt_bracode", "Table receipt_barcode did not get a primary key (record_id");
                    exception.printStackTrace();
                } finally {
                     mDatabase.endTransaction();
                }
    
    0 讨论(0)
  • 2020-11-28 06:37

    I used the CREATE TABLE AS syntax to merge several columns and encountered the same problem. Here is an AppleScript I wrote to speed the process up.

    set databasePath to "~/Documents/Databases/example.db"
    set tableOne to "separate" -- Table from which you are pulling data
    set tableTwo to "merged" -- Table you are creating
    set {tempCol, tempColEntry, permColEntry} to {{}, {}, {}}
    set permCol to {"id integer primary key"}
    
    -- Columns are created from single items  AND from the last item of a list
    -- {{"a", "b", "c"}, "d", "e"} Columns "a" and "b" will be merged into a new column "c".  tableTwo will have columns "c", "d", "e"
    
    set nonCoal to {"City", "Contact", "Names", {"Address 1", "Address", "address one", "Address1", "Text4", "Address 1"}, {"E-Mail", "E-Mail Address", "Email", "Email Address", "EmailAddress", "Email"}, {"Zip", "Zip Code", "ZipCode", "Zip"}, {"Telephone", "BusinessPhone", "Phone", "Work Phone", "Telephone"}, {"St", "State", "State"}, {"Salutation", "Mr/Ms", "Mr/s", "Salutations", "Sautation", "Salutation"}}
    
    -- Build the COALESCE statements
    repeat with h from 1 to count of nonCoal
    set aColumn to item h of nonCoal
    if class of aColumn is not list then
        if (count of words of aColumn) > 1 then set aColumn to quote & aColumn & quote
        set end of tempCol to aColumn
        set end of permCol to aColumn
    else
        set coalEntry to {}
        repeat with i from 1 to count of aColumn
            set coalCol to item i of aColumn as string
            if (count of words of coalCol) > 1 then set coalCol to quote & coalCol & quote
            if i = 1 then
                set end of coalEntry to "TRIM(COALESCE(" & coalCol & ", '') || \" \" || "
            else if i < ((count of aColumn) - 1) then
                set end of coalEntry to "COALESCE(" & coalCol & ", '') || \" \" || "
            else if i = ((count of aColumn) - 1) then
                set as_Col to item (i + 1) of aColumn as string
                if (count of words of as_Col) > 1 then set as_Col to quote & as_Col & quote
                set end of coalEntry to ("COALESCE(" & coalCol & ", '')) AS " & as_Col) & ""
                set end of permCol to as_Col
            end if
        end repeat
        set end of tempCol to (coalEntry as string)
    end if
    end repeat
    
    -- Since there are ", '' within the COALESCE statement, you can't use "TID" and "as string" to convert tempCol and permCol for entry into sqlite3. I rebuild the lists in the next block.
    repeat with j from 1 to count of tempCol
    if j < (count of tempCol) then
        set end of tempColEntry to item j of tempCol & ", "
        set end of permColEntry to item j of permCol & ", "
    else
        set end of tempColEntry to item j of tempCol
        set end of permColEntry to item j of permCol
    end if
    end repeat
    set end of permColEntry to ", " & item (j + 1) of permCol
    set permColEntry to (permColEntry as string)
    set tempColEntry to (tempColEntry as string)
    
    -- Create the new table with an "id integer primary key" column
    set createTable to "create table " & tableTwo & " (" & permColEntry & "); "
    do shell script "sqlite3 " & databasePath & space & quoted form of createTable
    
    -- Create a temporary table and then populate the permanent table
    set createTemp to "create temp table placeholder as select " & tempColEntry & " from " & tableOne & ";  " & "insert into " & tableTwo & " select Null, * from placeholder;"
    do shell script "sqlite3 " & databasePath & space & quoted form of createTemp
    
    --export the new table as a .csv file
    do shell script "sqlite3 -header -column -csv " & databasePath & " \"select * from " & tableTwo & " ; \"> ~/" & tableTwo & ".csv"
    
    0 讨论(0)
  • 2020-11-28 06:42

    I tried to add the primary key afterwards by changing the sqlite_master table directly. This trick seems to work. It is a hack solution of course.

    In short: create a regular (unique) index on the table, then make the schema writable and change the name of the index to the form reserved by sqlite to identify a primary key index, (i.e. sqlite_autoindex_XXX_1, where XXX is the table name) and set the sql string to NULL. At last change the table definition itself. One pittfal: sqlite does not see the index name change until the database is reopened. This seems like a bug, but not a severe one (even without reopening the database, you can still use it).

    Suppose the table looks like:

    CREATE TABLE tab1(i INTEGER, j INTEGER, t TEXT);
    

    Then I did the following:

    BEGIN;
    CREATE INDEX pk_tab1 ON tab1(i,j);
    pragma writable_schema=1;
    UPDATE sqlite_master SET name='sqlite_autoindex_tab1_1',sql=null WHERE name='pk_tab1';
    UPDATE sqlite_master SET sql='CREATE TABLE tab1(i integer,j integer,t text,primary key(i,j))' WHERE name='tab1';
    COMMIT;
    

    Some tests (in sqlite shell):

    sqlite> explain query plan select * from tab1 order by i,j;
    0|0|0|SCAN TABLE tab1 USING INDEX sqlite_autoindex_tab1_1
    sqlite> drop index sqlite_autoindex_tab1_1;
    Error: index associated with UNIQUE or PRIMARY KEY constraint cannot be dropped    
    
    0 讨论(0)
  • 2020-11-28 06:45

    I had the same problem and the best solution I found is to first create the table defining primary key and then to use insert into statement.

    CREATE TABLE mytable (
    field1 INTEGER PRIMARY KEY,
    field2 TEXT
    );
    
    INSERT INTO mytable 
    SELECT field1, field2 
    FROM anothertable;
    
    0 讨论(0)
提交回复
热议问题