How to optimize SQLite indexes for query - sqlite

I have a SQLite table as
CREATE TABLE T(
CategoryCode NVARCHAR(64) NOT NULL,
DateTime DateTime NOT NULL,
ItemCode NVARCHAR(64) NOT NULL,
ItemName NVARCHAR(64) NOT NULL,
ItemValue NUMERIC(28, 4) NOT NULL
)
The question is how to optimize indexes for the following query:
SELECT
CategoryCode
,ItemCode
,ItemName
,SUM(ItemValue) as TotalValue
FROM T
WHERE CategoryCode = 'Code1'
AND DateTime < '2012-01-04 00:00:00'
GROUP BY ItemCode
Thank you!

For the exact query, you will need an index on T(CategoryCode, DateTime) or T(DateTime, CategoryCode), depending on which column is more selective than the other.
However, it is unwise to create an index for a single query without a more holistic view on all access to the table.
e.g. You may find, for example, that if most data in the table has CategoryCode = 'Code1' then the index should only be created on the DateTime column.

Related

SQLite UPDATE only one record matching a WHERE clause, with MAX on another filed

Need to modify the data in an SQLite database that has fields similar to:
hash (string)
modTime (long, time in ms)
data (any data, e.g. string)
I know the hash value of the record to set the data for (it's really xxHash of some binary blob). There can be several records with the same hash, but I need to modify only the one with MAX(modTime) value with the matching hash... Can't figure out how to construct the UPDATE command.
You can use NOT EXISTS in the WHERE clause:
UPDATE tablename
SET data = new_data
WHERE hash = ?
AND NOT EXISTS (SELECT 1 FROM tablename t WHERE t.hash = tablename.hash AND t.modTime > tablename.modTime)
Or use the column rowid to find the row that you want to update:
UPDATE tablename
SET data = new_data
WHERE rowid = (SELECT rowid FROM tablename WHERE hash = ? ORDER BY modTime DESC LIMIT 1)
or with FIRST_VALUE() window function:
UPDATE tablename
SET data = new_data
WHERE rowid = (SELECT DISTINCT FIRST_VALUE(rowid) OVER (ORDER BY modTime DESC) FROM tablename WHERE hash = ?)
Replace new_data with the new value of data and ? with the value of hash you search for.

SQLite. Complex condition in DELETE query?

I have a table with no Primary Key columns. First column is of type DATETIME named as DateTime, the rest are of type NVARCHAR(16). Values in the column AlarmStatus can be either '0' or '1'.
As such, my table may have multiple rows with the same DateTime and different AlarmStatus values. I neeed a way to delete ALL the rows with the same DateTime ONLY IF no one of them has a '1' in AlarmStatus column.
DELETE
FROM yourTable
WHERE DateTime IN
(
SELECT DateTime
FROM yourTable
GROUP BY DateTime
HAVING SUM(CASE WHEN AlarmStatus = '1' THEN 1 ELSE 0 END) = 0
)
This can be done with a correlated subquery: You want to delete rows for which no row with the same DateTime value and a set AlarmStatus exists:
DELETE FROM MyTable
WHERE NOT EXISTS (SELECT *
FROM MyTable AS T2
WHERE T2.DateTime = MyTable.DateTime
AND T2.AlarmStatus = '1');
Alternatively, get a list of all timestamps with the status you want to keep, and check that the row to be deleted does not have one of those:
DELETE FROM MyTable
WHERE DateTime NOT IN (SELECT DateTime
FROM MyTable
WHERE AlarmStatus = '1');

Get names for IDs in two columns in one from another table

I have two tables:
CREATE TABLE tElements (
elementID INTEGER,
name TEXT,
area TEXT,
zone TEXT,
voltageLevel TEXT,
mRID TEXT
);
CREATE TABLE tCAResults (
timestamp INTEGER NOT NULL,
outageElementID INTEGER NOT NULL,
monitoredElementID INTEGER NOT NULL,
preOutageLoading DOUBLE NOT NULL,
postOutageLoading DOUBLE NOT NULL
);
How can I create query where id's of outageElementID and monitoredElementID from table tCAResult would be displayed as names from table tElements?
I have been searching for a whole day but couldn't find the answer. The closest I found is this but can't work it out
A simple join or two will do the job:
select tc.timestamp, oe.name as outageElement, me.name as monitoredElement
from tCAResults tc
join tElements oe on (oe.elementID = tc.outageELementID)
join tElements me on (me.elementID = tc.monitoredElementID);

Why one sqlite query is so much slower even though they are very similar?

>
sqlite> .timer on
query 1
sqlite> select count(*) from alpha where Name = 'SHOUT' and Date between 20130101 and 20140101;
3783443
CPU Time: user 42.067187 sys 2.098010
query 2
sqlite> select count(*) from alpha where Date between 20130101 and 20140101;
3783443
CPU Time: user 0.450523 sys 0.054451
Schema:
sqlite> .schema
CREATE TABLE alpha (
Date Date,
Name VARCHAR(50),
Symbol VARCHAR(10),
Value FLOAT,
ChangeDate DATETIME DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (Date,Name,Symbol) );
CREATE TABLE cusip (
Symbol VARCHAR(10),
Cusip VARCHAR(9),
PRIMARY KEY (Symbol) );
CREATE INDEX idx_alpha_Date on alpha (Date);
CREATE INDEX idx_alpha_Symbol on alpha (Symbol);
CREATE INDEX idx_alpha_date_name on alpha ( Date, Name );
CREATE INDEX idx_alpha_name on alpha (Name);
Use explain query plan to see how the indices are used and for more details, explain to see how it translates to sqlite virtual machine code.
sqlite> explain query plan select count(*) from alpha where Name = 'SHOUT' and Date between 20130101 and 20140101;
0|0|0|SEARCH TABLE alpha USING INDEX idx_alpha_name (Name=?) (~5 rows)
sqlite> explain query plan select count(*) from alpha where Date between 20130101 and 20140101;
0|0|0|SEARCH TABLE alpha USING COVERING INDEX idx_alpha_date_name (Date>? AND Date<?) (~31250 rows)
In the first case, an index is used only for the Name = 'SHOUT' part and Date between 20130101 and 20140101 is applied to all results in that intermediate result set, possibly taking a long time. In the latter case, the results can be obtained from an index alone, without needing to scan through an intermediate result set.

Single insertion of data on one date in SQL Server?

ALTER PROCEDURE [dbo].[K_FS_InsertMrpDetails]
#date datetime,
#feedtype varchar(50),
#rateperkg float,
#rateper50kg float,
#updatedby varchar(50)
AS
BEGIN
INSERT INTO K_FS_FeedMrpDetails([date], feedtype, rateperkg, rateper50kg, updatedby, updatedon)
VALUES(#date, #feedtype, #rateperkg, #rateper50kg, #updatedby, getdate())
SELECT '1' AS status
END
With this query we insert 9 rows at a time but what I want is in one same date do not insert again different details. How can I please help me.
Add a unique constraint on the column [date]. That will prevent you from adding more than one row with the same [date] value.
Update:
To allow 9 rows for each date you can add a computed column D that removes the time part and you need to add a column that will hold the values 1 to 9 R. Use a check constraint on R to only allow 1-9. Finally you create a unique constraint on (R, D).
Sample table definition:
create table T
(
ID int identity primary key,
DT datetime not null,
R tinyint check (R in (1,2,3,4,5,6,7,8,9)) not null,
D as dateadd(day, datediff(day, 0, DT), 0),
constraint ux_RD unique (R,D)
)
Try with this:
insert into T(DT, R) values(getdate(), 1)
insert into T(DT, R) values(getdate(), 2)
insert into T(DT, R) values(getdate(), 1)
First and second insert works fine, the third raises a unique constraint exception.

Resources