Having trouble running COUNT in my INSERT INTO statement - sqlite

I'm having a little trouble getting my head around this statement. The idea is it's meant to initialize a table with a single row of values for each player in the database, but I can't figure out from a browser full of search tabs what I'm doing wrong. All I know is apparently my syntax is rubbish.
INSERT INTO tblKebabs
(TransactionID, PlayerID, Amount, Description, Timestamp)
SELECT
(COUNT(tblPlayers.PlayerID)) AS TransactionID,
tblPlayers.PlayerID AS PlayerID,0 as Amount,
"Initializer" as Description,"now" AS Timestamp)
FROM tblPlayers
WHERE tblPlayers.PlayerID > 0;

If you want the TransactionID column to be AUTOINCREMENT you have to define it in the CREATE statement.
If you already have defined it as INTEGER PRIMARY KEY it is already AUTOINCREMENT and you don't need to change something.
If you have nothing of the above then you have to recreate the table with INTEGER PRIMARY KEY for this column because SQLite does not allow such changes with ALTER. Now you can omit this column from your statement:
INSERT INTO tblKebabs
(PlayerID, Amount, Description, Timestamp)
SELECT
PlayerID,
0 as Amount,
'Initializer',
CURRENT_TIMESTAMP
FROM tblPlayers
WHERE PlayerID > 0;
You don't need aliases in the SELECT statement.
Also I used CURRENT_TIMESTAMP.

I'm not sure what you're trying to do here but if it's just about the count, try this
ROW_NUMBER() OVER(ORDER BY tblPlayers.PlayerID) AS TransactionID

Related

Insert VALUES not already in table

I want to create a table and then initialize it with some values, in as concise manner as possible.
However, this script gets executed every time my app starts, so the insert should happen only on items that were not already added previously.
I do not want to use IGNORE directive in 'INSERT IGNORE INTO', because I do not want to ignore unexpected errors.
For some reason, INSERT INTO fails with "SQL error (1136): Column count doesn't match value count at row 1", even though the select that follows gives the values that need to be added.
Here's the failing code:
START TRANSACTION;
CREATE TABLE IF NOT EXISTS `privileges` (
`id` TINYINT NOT NULL AUTO_INCREMENT,
`label` VARCHAR(25) UNIQUE,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `privileges` (`label`)
SELECT `label` FROM (
SELECT NULL AS `label`
UNION VALUES
('item1'),
('item2')
) X
WHERE `label` IS NOT NULL
AND `label` NOT IN (SELECT `label` FROM `privileges`)
COMMIT;
Currently I am solving this by first inserting the values into a temporary table, and then performing a select on that. But why isn't the above working and is there a more concise way to do what I'm trying to do?
I'm using MariaDB 10.3.9, added missing UNIQUE constraint
Edit 2: Thanks to LukStorms for figuring out the error was related to AUTO_INCREMENT, it seems passing NULL for AUTO_INCREMENT column solves the problem like so:
INSERT INTO `privileges` (id, label)
WITH ITEMS(label) AS (VALUES
('users:read'),('users:create'),
('clients:read'),('clients:write'),
('catalog:read'),('catalog:write'),
('cart:read'),('cart:write'),
('orders:read'),('orders:write'), ('test1')
) SELECT NULL, label FROM ITEMS i
WHERE label NOT IN (SELECT label FROM `privileges`);
In MariaDb 10.3+, using a CTE with a the VALUES expression can let you assign a column name to it.
with ITEMS(label) as
(VALUES
('item1')
,('item2'))
select i.label
from ITEMS i
where not exists (select 1 from privileges p where p.label = i.label)
But somehow it gives an error when inserting into a table that has a field with an AUTO_INCREMENT. Seems like a bug to me.
However, when you insert a NULL into a an AUTO_INCREMENT field then the NULL gets ignored. But you discovered that behaviour yourself.
So this works:
INSERT INTO privileges (id, label)
WITH ITEMS(label) as (
VALUES ('item1'), ('item2')
)
SELECT null, i.label
FROM ITEMS i
WHERE NOT EXISTS (SELECT 1 FROM privileges p WHERE p.label = i.label);
Test on db<>fiddle here
Using unioned selects also works though.
INSERT INTO privileges (label)
SELECT label
FROM (
SELECT 'item1' as label UNION ALL
SELECT 'item2'
) i
WHERE NOT EXISTS (SELECT 1 FROM privileges p WHERE p.label = i.label);
db<>fiddle here
Maybe another way is to use a temporary table (that will vanish when the session expires)
CREATE TEMPORARY TABLE tmp_items (label VARCHAR(25) NOT NULL PRIMARY KEY);
INSERT INTO tmp_items (label) VALUES
('item1')
,('item2');
INSERT INTO privileges (label)
SELECT label
FROM tmp_items i
WHERE label NOT IN (SELECT DISTINCT label FROM privileges);
Test on db<>fiddle here
First, your application is trying to double-insert values. It probably shouldn't be doing that (though I can think of a few valid use cases). Consider making it so that it does not try to add data that it's already added before. If you don't have easy access to inter-instance state, pull the current list out of the database on startup before deciding what to insert.
Second, if you want labels to be unique, why is there not a unique key on the label field? At the moment, INSERT IGNORE wouldn't even work because there is nothing in your schema preventing duplicate label values. I would ask yourself why you need an auto-incrementing ID: why not just have the label, and make it the primary key?
Then, if you still need to do this duplicate-elision at the SQL layer, you may use ON DUPLICATE KEY to suck up redundant inserts of an existing primary key:
INSERT INTO `privileges` (`label`)
VALUES
('item1'),
('item2')
)
ON DUPLICATE KEY UPDATE `label` = `label`
This solution is difficult to implement with your auto-increment ID key, because your application probably doesn't know what the ID is going to be. Another reason to consider dropping it.
Unfortunately, there's no ON DUPLICATE KEY IGNORE.
If you want to keep the ID key, and you don't want your application to do a read step on startup (perhaps for scalability reasons), then INSERT IGNORE to be quite honest is your best bet, though you're still going to need at least a unique key on label to make that work.

How to read the last record in SQLite table?

Is there a way to read the value of the last record inserted in an SQLite table without going through the previous records ?
I ask this question for performance reasons.
There is a function named sqlite3_last_insert_rowid() which will return the integer key for the most recent insert operation. http://www.sqlite.org/c3ref/last_insert_rowid.html
This only helps if you know the last insert happened on the table you care about.
If you need the last row on a table, regardless of wehter the last insert was on this table or not, you will have to use a SQL query
SELECT * FROM mytable WHERE ROWID IN ( SELECT max( ROWID ) FROM mytable );
When you sort the records by ID, in reverse order, the last record will be returned first.
(Because of the implicit index on the autoincrementing column, this is efficient.)
If you aren't interested in any other records, use LIMIT:
SELECT *
FROM MyTable
ORDER BY _id DESC
LIMIT 1

SQLite: Ordering my select results

I have a table with unique usernames and a bunch of string data I am keeping track of. Each user will have 1000 rows and when I select them I want to return them in the order they were added. Is the following code a necessary and correct way of doing this:
CREATE TABLE foo (
username TEXT PRIMARY KEY,
col1 TEXT,
col2 TEXT,
...
order_id INTEGER NOT NULL
);
CREATE INDEX foo_order_index ON foo(order_id);
SELECT * FROM foo where username = 'bar' ORDER BY order_id;
Add a DateAdded field and default it to the date/time the row was added and sort on that.
If you absolutely must use the order_ID, which I don't suggest. Then at least make it an identity column. The reason I advise against this is because you are relying on side affects to do your sorting and it will make your code harder to read.
If each user will have 1000 rows, then username should not be the primary key. One option is to use the int identity column which all tables have (which optimizes I/O reads since it's typically stored in that order).
Read under "RowIds and the Integer Primary Key" # http://www.sqlite.org/lang_createtable.html
The data for each table in SQLite is stored as a B-Tree structure
containing an entry for each table row, using the rowid value as the
key. This means that retrieving or sorting records by rowid is fast.
Because it's stored in that order in the B-tree structure, it should be fast to order by the int primary key. Make sure it's an alias for rowid though - more in that article.
Also, if you're going to be doing queries where username = 'bob', you should consider an index on the username column - especially there's going to be many users which makes the index effective because of high selectivity. In contrast, adding an index on a column with values like 1 and 0 only leads to low selectivity and renders the index very ineffective. So, if you have 3 users :) it's not worth it.
You can remove the order_id column & index entirely (unless you need them for something other than this sorting).
SQLite tables always have a integer primary key - in this case, your username column has silently been made a unique key, so the table only has the one integer primary key. The key column is called rowid. For your sorting purpose, you'll want to explicitly make it AUTOINCREMENT so that every row always has a higher rowid than older rows.
You probably want to read http://www.sqlite.org/autoinc.html
CREATE TABLE foo (
rowid INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE KEY,
...
Then your select becomes
select * from foo order by rowed;
One advantage of this approach is that you're re-using the index SQLite will already be placing on your table. A date or order_id column is going to mean an extra index, which is just overhead here.

Is it possible to order by latest row insert desc?

Let me explain this a bit further.
I have a table in sqlite that looks something like this:
table
------
id numeric primary key,
uuid text not null,
other_field text
Now id is the standard auto-increment type. This table can have entries added and removed at any time so if a rowid that had been used once before was used again, that'd be fine. I'm not using that 'table full' feature of sqlite. There are allowed to be multiple entries with the same uuid. The idea is I'm only interested in the last inserted entry in general.
This raises the question. I know I can do a call like
"select other_field from table where uuid=? order by rowid desc"
This would be ok, but what if rowid wraps around? order by rowid desc will not give me the newest entry.
All I can think of is to add a creation_time field like
table
------
id numeric primary key,
uuid text not null,
other_field text
creation_time datetime
and then when it gets created put datetime('now') in that field.
select other_field from table where uuid=? order by creation_time desc
But this means adding an extra field and a bigger index. Is there a built in way to do this?
If you want to select the newest ID or RowID, have a look into the command LIMIT. With the LIMIT you can return only 1 row. For your example:
SELECT * from table ORDER BY id DESC LIMIT 1
With this solution, you don't have to add another field "creation_time".
Hops this helps :)

Keep first and remove dupliciate rows only using sqlite

Maybe i should do this in C# but i have more then one row with linkId X. I would like to remove it but i am unsure how. In code i could just use a foreach from 0 to n and remove any found rows with a greater (or !=) id but thats in code. Is there a less difficult way of doing it using sqlite?
Assuming the table's name is tableName and there is a primary key field named id, the following sql would do it. I think the following SQL query is general enough and should be able to be executed under any database engine.
delete from tableName
where id not in (
select min(id) from tableName
group by linkId
)

Resources