SQLite: select max index for each group - sqlite

What I'm trying to do:
SELECT MAX(index), scr FROM history WHERE state = "TQA" GROUP BY scr
So, for every 'scr' in the table, I want a row showing the maximum index where the 'state' of that row = 'TQA'.
For some reason this gives me a syntax error near "index".

INDEX is a reserved keyword in SQLite. If you want to use that as a field name, you'll need to quote it;
SELECT MAX("index"), scr FROM history WHERE state = "TQA" GROUP BY scr

Related

Converting a field to lower case and merging data in an sqlite database

I need to merge some randomly uppercased data that has been collected in an SQLite table key_val, such that key is always lowercase and no vals are lost. There is a unique compound index on key,val.
The initial data looks like this:
key|val
abc|1
abc|5
aBc|1
aBc|5
aBc|3
aBc|2
AbC|1
abC|3
The result after the merge would be
key|val
abc|1
abc|2
abc|3
abc|5
In my programmer brain, I would
for each `key` with upper case letters;
if a lower cased `key` is found with the same value
then delete `key`
else update `key` to lower case
Re implementing the loop has a sub query for each row found with upper case letters, to check if the val already exists as a lower case key
If it does, I can delete the cased key.
From there I can UPDATE key = lower(key) as the "duplicates" have been removed.
The first cut of the programming method of finding the dupes is:
SELECT * FROM key_val as parent
WHERE parent.key != lower(parent.key)
AND 0 < (
SELECT count(s.val) FROM key_val as s
WHERE s.key = lower(parent.key) AND s.val = parent.val
)
ORDER BY parent.key DESC;
I'm assuming there's a better way to do this in SQLite? The ON CONFLICT functionality seems to me like it should be able to handle the dupe deletion on UPDATE but I'm not seeing it.
First delete all the duplicates:
DELETE FROM key_val AS k1
WHERE EXISTS (
SELECT 1
FROM key_val AS k2
WHERE LOWER(k2.key) = LOWER(k1.key) AND k2.val = k1.val AND k2.rowid < k1.rowid
);
by keeping only 1 combination of key and val with the min rowid.
It is not important if you kept the key with all lower chars or not, because the 2nd step is to update the table:
UPDATE key_val
SET key = LOWER(key);
See the demo.
Honestly it might just be easier to create a new table and then insert into it. As it seems you really just want a distinct select here, use:
INSERT INTO kev_val_new ("key", val)
SELECT DISTINCT LOWER("key"), val
FROM key_val;
Once you have populated the new table, you may drop the old one, and then rename the new one to the previous name:
DROP TABLE key_val;
ALTER TABLE key_val_new RENAME TO key_val;
I agree with #Tim that it would be easire to re-create table using simple select distict lower().. statement, but that's not always easy if table has dependant objects (indexes, triggers, views). In this case this can be done as sequence of two steps:
insert lowered keys which are not still there:
insert into t
select distinct lower(tr.key) as key, tr.val
from t as tr
left join t as ts on ts.key = lower(tr.key) and ts.val = tr.val
where ts.key is null;
now when we have all lowered keys - remove other keys:
delete from t where key <> lower(key);
See fiddle: http://sqlfiddle.com/#!5/84db50/11
However this method assumes that key is always populated (otherwise it would be a strange key)
If vals can be null then "ts.val = tr.val" should be replaced with more complex stuff like ifnull(ts.val, -1) = ifnull(tr.val, -1) where -1 is some unused value (can be different). If we can't assume any unused value like -1 then it should be more complex check for null / not null cases.

SQLite3 creating a view profit combining rows twice based on column

Given a table
symbol|action|quantity|price
foo|buy|1|-10.00
foo|sell|1|10.50
bar|buy|1|-50.00
bar|sell|1|50.50
I am trying to create a view that shows profit per symbol
I've tried several variations of below with multiple 'order by' but not getting the expected output
create view if not exists profit_view as select symbol,sum(price * quanity) as profit from trans group by symbol;
What I'd like is a view that shows or something close to that
symbol|profit|percent
foo|.50|5
Assuming each symbol only has a single buy and sell record we can try:
SELECT
symbol,
SUM(quantity*price) AS profit,
100.0 * SUM(quantity*price) /
ABS(MAX(CASE WHEN action = 'buy' THEN quantity*price END)) AS percent
FROM trans
GROUP BY
symbol;

Update row with value from next row sqlite

I have the following columns in a SQLite DB.
id,ts,origin,product,bid,ask,nextts
1,2016-10-18 20:20:54.733,SourceA,Dow,1.09812,1.0982,
2,2016-10-18 20:20:55.093,SourceB,Oil,7010.5,7011.5,
3,2016-10-18 20:20:55.149,SourceA,Dow,18159.0,18161.0,
How can I populate the 'next timestamp' column (nextts) with the next timestamp for the same product (ts), from the same source? I've been trying the following, but I can't seem to put a subquery in an UPDATE statement.
UPDATE TEST a SET nextts = (select ts
from TEST b
where b.id> a.id and a.origin = b.origin and a.product = b.product
order by id asc limit 1);
If I call this, I can display it, but I haven't found a way of updating the value yet.
select a.*,
(select ts
from TEST b
where b.id> a.id and a.origin = b.origin and a.product = b.product
order by id asc limit 1) as nextts
from TEST a
order by origin, a.id;
The problem is that you're using table alias for table in UPDATE statement, which is not allowed. You can skip alias from there and use unaliased (but table-name prefixed) reference to its columns (while keeping aliased references for the SELECT), like this:
UPDATE TEST
SET nextts = (
SELECT b.ts
FROM TEST b
WHERE b.id > TEST.id AND
TEST.origin = b.origin AND
TEST.product = b.product
ORDER BY b.id ASC
LIMIT 1
);
Prefixing unaliased column references with the table name is necessary for SQLite to identify that you're referencing to unaliased table. Otherwise the id column whould be understood as the id from the closest[*] possible data source, in which case it's the aliased table (as b alias), while we're interested in the unaliased table, therefore we need to explicitly tell SQLite that.
[*] Closest data source is the one listed in the same query, or parent query, or parent's parent query, etc. SQLite is looking for the first data source (going from inner part to the outside) in the query hierarchy that defines this column.

MS Acess: max(Date/Time field) on query when field may contain 00:00:00

I am trying to build a Query in MS Access that returns the last date/time for a given entity ID. Research shows that using the MAX() function on the corresponding field and using GROUP BY on the remaining fields appears to be the way to go.
However, this doesn't seem to work in the presence of values that hold 0 hours, 0 minutes and 0 seconds, as it shows those values as well. The query's SQL is as follows:
SELECT Int(Historico_Classificacoes.ID_Entidade) AS ID_Entidade, Max(Historico_Classificacoes.Timestamp_Classificacao) AS [Data da última classificação], Historico_Classificacoes.US_Indicia_Pais_Constituicao, Historico_Classificacoes.US_Indicia_Responsabilidades_Fiscais, Historico_Classificacoes.US_Indicia_Morada_Coletiva, Historico_Classificacoes.US_Indicia_Telefone, Historico_Classificacoes.US_Indicia_Proveniencia_Capital, Historico_Classificacoes.US_Indicia_Beneficiários, Historico_Classificacoes.US_Indicia_Naturalidade, Historico_Classificacoes.US_Indicia_Nacionalidade, Historico_Classificacoes.US_Indicia_Morada_Singular, Historico_Classificacoes.US_Indicia_Laboral
FROM Historico_Classificacoes
GROUP BY Int(Historico_Classificacoes.ID_Entidade), Historico_Classificacoes.US_Indicia_Pais_Constituicao, Historico_Classificacoes.US_Indicia_Responsabilidades_Fiscais, Historico_Classificacoes.US_Indicia_Morada_Coletiva, Historico_Classificacoes.US_Indicia_Telefone, Historico_Classificacoes.US_Indicia_Proveniencia_Capital, Historico_Classificacoes.US_Indicia_Beneficiários, Historico_Classificacoes.US_Indicia_Naturalidade, Historico_Classificacoes.US_Indicia_Nacionalidade, Historico_Classificacoes.US_Indicia_Morada_Singular, Historico_Classificacoes.US_Indicia_Laboral
ORDER BY Int(Historico_Classificacoes.ID_Entidade);
The Historico_Classificacoes table currently holds the following data:
"ID_Entidade";"Timestamp_Classificacao";"Classificacao_DMIF";"Notacao_Risco_BCFT";"US_Indicia_Pais_Constituicao";"US_Indicia_Responsabilidades_Fiscais";"US_Indicia_Morada_Coletiva";"US_Indicia_Telefone";"US_Indicia_Proveniencia_Capital";"US_Indicia_Beneficiários";"US_Indicia_Naturalidade";"US_Indicia_Nacionalidade";"US_Indicia_Morada_Singular";"US_Indicia_Laboral"
"62";20/9/2015 00:00:00;1;30;1;1;1;1;1;1;1;1;1;0
"62";28/9/2015 10:43:38;1;30;1;1;1;1;1;1;1;1;1;1
"62";29/9/2015 17:52:24;1;30;1;1;1;1;1;1;1;1;1;1
"62";29/9/2015 17:52:40;1;30;1;1;1;1;1;1;1;1;1;1
"98";20/9/2015 00:00:00;2;15;1;1;1;1;1;1;0;0;0;0
"98";20/9/2015 00:00:01;0;0;0;0;0;0;0;0;0;0;0;0
The query, when executed in Datasheet View, outputs the following:
"ID_Entidade";"Data da última classificação";"US_Indicia_Pais_Constituicao";"US_Indicia_Responsabilidades_Fiscais";"US_Indicia_Morada_Coletiva";"US_Indicia_Telefone";"US_Indicia_Proveniencia_Capital";"US_Indicia_Beneficiários";"US_Indicia_Naturalidade";"US_Indicia_Nacionalidade";"US_Indicia_Morada_Singular";"US_Indicia_Laboral"
62;29/9/2015 17:52:40;1;1;1;1;1;1;1;1;1;1
62;20/9/2015 00:00:00;1;1;1;1;1;1;1;1;1;0
98;20/9/2015 00:00:00;1;1;1;1;1;1;0;0;0;0
98;20/9/2015 00:00:01;0;0;0;0;0;0;0;0;0;0
There are duplicated records for entities 62 and 98, when only one record for each was expected. Am I missing something here? Why are the entries whose values hold 00:00:00 present?
You may want to consider using an additional query as an intermediate step that identifies the MAX Date/Time combination for each group ID first, then a follow up query that pulls the entire record where that Group ID, Date and Time match, this will ensure you won't have to use First or Min on the rest of your fields, and you will always get the correct data
You use Group By for the last fields like US_Indicia_Morada_Singular and US_Indicia_Laboral. You'll have to use First, Last, Min, or Max on these as well.
Here is your attempt (without the repeated alias)
SELECT INT(ID_Entidade) AS ID_Entidade
, MAX(Timestamp_Classificacao) AS [Data da última classificação]
, US_Indicia_Pais_Constituicao
, US_Indicia_Responsabilidades_Fiscais
, US_Indicia_Morada_Coletiva
, US_Indicia_Telefone
, US_Indicia_Proveniencia_Capital
, US_Indicia_Beneficiários
, US_Indicia_Naturalidade
, US_Indicia_Nacionalidade
, US_Indicia_Morada_Singular
, US_Indicia_Laboral
FROM Historico_Classificacoes
GROUP BY INT(ID_Entidade)
, US_Indicia_Pais_Constituicao
, US_Indicia_Responsabilidades_Fiscais
, US_Indicia_Morada_Coletiva
, US_Indicia_Telefone
, US_Indicia_Proveniencia_Capital
, US_Indicia_Beneficiários
, US_Indicia_Naturalidade
, US_Indicia_Nacionalidade
, US_Indicia_Morada_Singular
, US_Indicia_Laboral
ORDER BY INT(ID_Entidade);
From you comments, here is SQL that is close to what you need. I have added the field "AnotherField" for you as you may or may not need to add field here.
This currently selects the whole record from the table, but only the single "most recent" record for each value found in the AnotherField is listed.
It may be that you need more that one field where AnotherField appears in the SQL. Think of the field you use instead of AnotherField as being the fields that need to be used to find the maximum date record.
SELECT Main.*
FROM Historico_Classificacoes AS Main
INNER JOIN ( SELECT AnotherField
, MAX(Timestamp_Classificacao) AS [MaxDate]
FROM Historico_Classificacoes
GROUP BY AnotherField
)
AS MostRecent
ON ( Main.AnotherField = MostRecent.AnotherField
AND
Main.Timestamp_Classificacao = MostRecent.MaxDate
)

SQLITE getting trigger to check against table1 and insert into table2

I have a reservation table which the user will insert records into.
The User does not select the car just the type.
Once the record is inserted I want some triggers to do the following:-
The date and vehicle combination to a log file the data and vehicle are UNIQUE(date,car) so the same reservation cannot be made twice therefore a car cannot be double booked.
SO my issue is how do I now get the trigger to select the next available car of that type?
I can get it to select the next car by just saying Car.carid != Log.carid but that is not using the date as a second check so I could not then use the vehicle on another date.
Simply putting AND between the check doesnt work meaning
WHERE Car.carid != Log.carid AND Reservation.date != C.datewhen
Some guidance would be much appreciated!
I think this query is what you are after...
SELECT MIN(c.carid)
FROM all_cars c
WHERE c.type = 'CAR TYPE'
AND NOT EXISTS
(SELECT 1
FROM reservation r
WHERE r.date = 'THE DATE'
AND r.carid = c.carid)
If the query returns a NULL value then no cars are available.
Or you could take off the MIN and just use the first record returned.

Resources