sqlite alter using select and match from other tables - sqlite

I know how to do these sorts of things using perl, python or even MySQL but I can't not seem to figure out how to do this with sqlite. Hoping maybe somebody here can help.
UPDATED NOTE: I'm limited to sqlite version 2.8.17
I have:
create table Ta (
a1 INTEGER PRIMARY KEY,
a2 VARCHAR(12) );
create table Tb (
b1 VARCHAR(12) PRIMARY KEY,
b2 INTEGER,
b3 VARCHAR(8),
b4 VARCHAR(8) );
What I would like to do via the command line and in a basic sql script is this:
Go through all of the rows in Tb and where b2 == a1, I would like to replace the value stored in b1 with the cooresponding value in a2.
Simplified it's something like:
b1 = select a2 from Ta where a1 = b2
Any ideas?

How about this?
UPDATE Tb
SET
b1 = (SELECT a2
FROM Ta
WHERE Tb.b2 = Ta.a1 )
WHERE
EXISTS (
SELECT *
FROM Ta
WHERE Tb.b2 = Ta.a1 );

Unless I missed something in your question, you simply have to update:
update tb set b1 = (select a2 from Ta where a1 = b2);
UPDATE 1
OP mentions that she/he is using sqlite 2.8.17, so "cross-table" updates are not supported.
I found this link which provides a workaround. It requires that the field joined on be primary keys, which is the case for this question.
Here is the statement:
insert or replace into tb (b2, b1)
select ta.a1, ta.a2
from ta, tb
where ta.a1=tb.b2;
I haven't tested it otherwise then verifying that it executes without errors. To the best of my SQL knowledge it should do the same as the update statement I posted prior to this update.
UPDATE 2
There is a problem with the above as the OP pointed out. It will insert new records rather than update existing records in Tb. I do now see a contradiction in what the OP is trying to do:
Assume that sqlite 3.x.y is used. A simple update statement can get the job done. The problem is that it will fail as soon as more than one record in Tb has the same b2 value that exists in Ta.a1:
sqlite> create table ta (
...> a_key INTEGER PRIMARY KEY,
...> a_val TEXT);
sqlite> create table tb (
...> b_key TEXT PRIMARY KEY,
...> b_val INTEGER);
sqlite> insert into ta values (1, 'a');
sqlite> insert into tb values ('z', 1);
sqlite> insert into tb values ('y', 1);
sqlite> update tb set b_key=(select a_val from ta where a_key=b_val);
Error: column b_key is not unique
So the solution here is to make Tb1.b2 unique:
create table Tb (
b1 VARCHAR(12) PRIMARY KEY,
b2 INTEGER UNIQUE,
b3 VARCHAR(8),
b4 VARCHAR(8));
Making Tb.b2 unique makes the solution in my first update work properly and prevents the uniqueness violation exposed above.

Related

sqlite3 : Compare two cells from the same table

I have a table like this
CREATE TABLE "modules" ( `ID` INTEGER PRIMARY KEY AUTOINCREMENT, `name` TEXT, `version` TEXT, `deployID` INTEGER )
I want to be able to get all the rows from this database where the version is different between different deploy ID's. So, say I have a deploy ID 2 and a deployID 3 - my table might have 2000 odd modules for each of these deploys. I want to only get the rows where the name is the same but the version is different. Is this possible? I thought this query would do it but it seems to be returning me everything - twice!
SELECT a.* FROM modules a
INNER JOIN modules b
ON a.name == b.name
WHERE a.version != b.version
AND a.deployID = 3
AND b.deployID = 2
If you only care about deployids 2 and 3:
select m.*
from modules m
where
m.deployid in (2, 3)
and
exists (
select 1 from modules
where
name = m.name
and
deployID <> m.deployID
and
version <> m.version
)
If you don't need this condition:
deployID <> m.deployID
you can remove it.
I would probably use an exists query here:
SELECT m1.*
FROM modules m1
WHERE EXISTS (SELECT 1 FROM modules m2
WHERE m1.name = m2.name AND
m1.deployID <> m2.deployID AND m1.version <> m2.version);
Or maybe you want a more specific version:
SELECT m1.*
FROM modules m1
WHERE EXISTS (SELECT 1 FROM modules m2
WHERE m1.name = m2.name AND
LEAST(m1.deployID, m2.deployID) = 2 AND
GREATEST(m1.deployID, m2.deployID) = 3);

How to write an update statement for which number of updated columns is dynamic

I want to write an UPDATE statement in which the number of columns may vary for each run, based on the user choice.
Eg: DDL
CREATE TABLE "XX_MASK_REF"
( "TABLE_NAME" VARCHAR2(150 BYTE),
"COLUMN_NAME" VARCHAR2(150 BYTE),
"FLAG" VARCHAR2(2 CHAR) DEFAULT 'N'
);
A predefined set of table names and the respective column names are inserted into this table. The default value for the FLAG is set to 'N'.The user depends on his requirement will set FLAG to 'Y' which will vary for each run.
Sample data: Run 1
Table_Name Column_Name Flag
T1 C1 Y
T1 C2 Y
T1 C3 N
T2 C1 N
T2 C2 Y
Sample data: Run 2
Table_Name Column_Name Flag
T1 C1 Y
T1 C2 N
T1 C3 N
T2 C1 N
T2 C2 Y
I need an UPDATE statement which should run only for those columns to which the FLAG is set to Y group by table_name. From the dataset given above, In the Run 1, the No of columns for table T1 is 2 whereas in the Run 2 it is only one column that needs to be updated.
The update statement generated should be able to update all the records available in the table which means where clause is not required.
The values to be updated will be available in variables. I intended to use the TRANSLATE function through which the values will be passed to the column to the column to be updated.
I think something like this would help me
V_CHAR := 'Update' ||' ' ||V_TABLE_NAME||' ' || 'Set' ||' ' || V_COLUMN_NAME||'='||' ' || 'TRANSLATE('||V_COLUMN_NAME||', '1234567890','abcdefghijk')
Please advise me if i miss anything or unclear
As APC commented, your question is incomplete, It does not mention where the update values and the where clause for each table comes from.
If you are simply looking for generating update statements without where clauses to run manually after putting appropriate changes, you may use a simple select query like this
SELECT
'UPDATE '
|| table_name
|| ' SET '
||
LISTAGG(column_name
|| ' = :'
|| ROWNUM,',') WITHIN GROUP(
ORDER BY
column_name
)||';'
AS v_upd_set
FROM
xx_mask_ref
WHERE
flag = 'Y'
GROUP BY
table_name
This will display rows with bind arguments to be used for passing values while running them.
UPDATE_QUERY
-------------------------------
UPDATE T1 SET C1 = :1,C2 = :2;
UPDATE T2 SET C2 = :3;
If you don't want this solution, edit your question and provide us more details.

how to select the same content of the field and to delete it in sqlite?

C:\Users\pengsir>sqlite3 e:\\test.db
sqlite> create table test (f1 TEXT,f2 TEXT, f3 TEXT);
sqlite> insert into test values("x1","y1","w1");
sqlite> insert into test values("x1","y1","w2");
sqlite> insert into test values("x1","y3","w2");
sqlite> insert into test values("x2","y3","w2");
sqlite> insert into test values("x3","y4","w4");
sqlite> insert into test values("x2","y3","w4");
sqlite> insert into test values("x1","y3","w2");
sqlite>
1.select the record rows which contain the same f1 and f2 ,and the rowid .
sqlite> select rowid,f1,f2 from test group by f1,f2 having(count(f2)>1 and count(f2)>1);
2|x1|y1
7|x1|y3
6|x2|y3
I want the result to be :
1|x1|y1
2|x1|y1
3|x1|y3
4|x2|y3
6|x2|y3
7|x1|y3
2.select the record rows which contain the same f1 f2 and f3,and the rowid .
sqlite> select rowid,f1,f2,f3 from test group by f1,f2,f3 having(count(f2)>1 and count(f3)>1);
7|x1|y3|w2
I want the result to be
3|x1|y3|w2
7|x1|y3|w2
let us discuss this problem further , i want to delete one |x1|y3|w2 and keep one |x1|y3|w2 in the table?here is my method.
DELETE FROM test
WHERE rowid in(
SELECT rowid FROM test
WHERE (SELECT count(*)
FROM test AS t2
WHERE t2.f1 = test.f1
AND t2.f2 = test.f2
AND t2.f3 = test.f3
) >= 2 limit 1);
Is there more simple and smart way to do that? (the method is wrong)
I find the proper way to do .
delete from test
where rowid not in
(
select max(rowid)
from test
group by
f1,f2,f3
);
and the method to more than one duplicate for a f1/f2 combination is :
delete from test
where rowid not in
(select rowid from test group by f1,f2);
It will be executed only one time.
You want records that have duplicates (in those fields), i.e., where the number of records with the same values in those fields is at least two:
SELECT rowid, f1, f2
FROM test
WHERE (SELECT count(*)
FROM test AS t2
WHERE t2.f1 = test.f1
AND t2.f2 = test.f2
) >= 2
This requires executing the subquery for each record.
Alternatively, compute the records with duplicates in a subquery once; this might be more efficient:
SELECT test.rowid, test.f1, test.f2
FROM test
JOIN (SELECT f1, f2
FROM test
GROUP BY f1, f2
HAVING count(*) >= 2
) USING (f1, f2)
If you want to remove one of the duplicates, this is easier to do because GROUP BY already returns exactly one output row for each group:
DELETE FROM test
WHERE rowid IN (SELECT max(rowid)
FROM test
GROUP BY f1, f2
HAVING COUNT(*) >= 2)
(If there is more than one duplicate for a f1/f2 combination, you have to execute this multiple times.)
Try using SELECT ALL instead of SELECT but according to the linked docs SELECT should behave like SELECT ALL by default and not like SELECT DISTINCT so I don't know where the problem may be.

SQLITE equivalent for Oracle's ROWNUM?

I'm adding an 'index' column to a table in SQLite3 to allow the users to easily reorder the data, by renaming the old database and creating a new one in its place with the extra columns.
The problem I have is that I need to give each row a unique number in the 'index' column when I INSERT...SELECT the old values.
A search I did turned up a useful term in Oracle called ROWNUM, but SQLite3 doesn't have that. Is there something equivalent in SQLite?
You can use one of the special row names ROWID, OID or _ROWID_ to get the rowid of a column. See http://www.sqlite.org/lang_createtable.html#rowid for further details (and that the rows can be hidden by normal columns called ROWID and so on).
Many people here seems to mix up ROWNUM with ROWID. They are not the same concept and Oracle has both.
ROWID is a unique ID of a database ROW. It's almost invariant (changed during import/export but it is the same across different SQL queries).
ROWNUM is a calculated field corresponding to the row number in the query result. It's always 1 for the first row, 2 for the second, and so on. It is absolutely not linked to any table row and the same table row could have very different rownums depending of how it is queried.
Sqlite has a ROWID but no ROWNUM. The only equivalent I found is ROW_NUMBER() function (see http://www.sqlitetutorial.net/sqlite-window-functions/sqlite-row_number/).
You can achieve what you want with a query like this:
insert into new
select *, row_number() over ()
from old;
No SQLite doesn't have a direct equivalent to Oracle's ROWNUM.
If I understand your requirement correctly, you should be able to add a numbered column based on ordering of the old table this way:
create table old (col1, col2);
insert into old values
('d', 3),
('s', 3),
('d', 1),
('w', 45),
('b', 5465),
('w', 3),
('b', 23);
create table new (colPK INTEGER PRIMARY KEY AUTOINCREMENT, col1, col2);
insert into new select NULL, col1, col2 from old order by col1, col2;
The new table contains:
.headers on
.mode column
select * from new;
colPK col1 col2
---------- ---------- ----------
1 b 23
2 b 5465
3 d 1
4 d 3
5 s 3
6 w 3
7 w 45
The AUTOINCREMENT does what its name suggests: each additional row has the previous' value incremented by 1.
I believe you want to use the constrain LIMIT in SQLite.
SELECT * FROM TABLE can return thousands of records.
However, you can constrain this by adding the LIMIT keyword.
SELECT * FROM TABLE LIMIT 5;
Will return the first 5 records from the table returned in you query - if available
use this code For create Row_num 0....count_row
SELECT (SELECT COUNT(*)
FROM main AS t2
WHERE t2.col1 < t1.col1) + (SELECT COUNT(*)
FROM main AS t3
WHERE t3.col1 = t1.col1 AND t3.col1 < t1.col1) AS rowNum, * FROM Table_name t1 WHERE rowNum=0 ORDER BY t1.col1 ASC

Correct SQLite syntax - UPDATE SELECT with WHERE EXISTS

I am trying to update a selected values in a column in a SQLite table. I only want update of the cells in the maintable where the criteria are met, and the cells must be updated to individual values, taken from a subtable.
I have tried the following syntax, but I get only a single cell update. I have also tried alternatives where all cells are updated to the first selected value of the subtable.
UPDATE maintable
SET value=(SELECT subtable.value FROM maintable, subtable
WHERE maintable.key1=subtable.key1 AND maintable.key2=subtable.key2)
WHERE EXISTS (SELECT subtable.value FROM maintable, subtable
WHERE maintable.key1=subtable.key1 AND maintable.key2=subtable.key2)
What is the appropriate syntax?
You can do this with an update select, but you can only do one field at a time. It would be nice if Sqlite supported joins on an update statement, but it does not.
Here is a related SO question, How do I UPDATE from a SELECT in SQL Server?, but for SQL Server. There are similar answers there.
sqlite> create table t1 (id int, value1 int);
sqlite> insert into t1 values (1,0),(2,0);
sqlite> select * from t1;
1|0
2|0
sqlite> create table t2 (id int, value2 int);
sqlite> insert into t2 values (1,101),(2,102);
sqlite> update t1 set value1 = (select value2 from t2 where t2.id = t1.id) where t1.value1 = 0;
sqlite> select * from t1;
1|101
2|102
In this case, it only updates one value from subtable per each raw from maintable.
The error is when subtable is include into of SELECT sentence.
UPDATE maintable
SET value=(SELECT subtable.value
FROM subtable
WHERE maintable.key1=subtable.key1 );
By default update with joins does not exist in SQLite; But we can use the with-clause + column-name-list + select-stmt from https://www.sqlite.org/lang_update.html to make something like this:
CREATE TABLE aa (
_id INTEGER PRIMARY KEY,
a1 INTEGER,
a2 INTEGER);
INSERT INTO aa VALUES (1,10,20);
INSERT INTO aa VALUES (2,-10,-20);
INSERT INTO aa VALUES (3,0,0);
--a bit unpleasant because we have to select manually each column and it's just a lot to write
WITH bb (_id,b1, b2)
AS (SELECT _id,a1+2, a2+1 FROM aa WHERE _id<=2)
UPDATE aa SET a1=(SELECT b1 FROM bb WHERE bb._id=aa._id),a2=(SELECT b2 FROM bb WHERE bb._id=aa._id)
WHERE _id in (SELECT _id from bb);
--soo now it should be (1,10,20)->(1,12,21) and (2,-10,-20)->(2,-8,-19), and it is
SELECT * FROM aa;
--even better with one select for each row!
WITH bb (_id,b1, b2)
AS (SELECT _id,a1+2, a2+1 from aa WHERE _id<=2)
UPDATE aa SET (a1,a2)=(SELECT b1,b2 FROM bb WHERE bb._id=aa._id)
WHERE _id in (SELECT _id from bb);
--soo now it should be (1,12,21)->(1,14,22) and (2,-8,-19)->(2,-6,-18), and it is
SELECT * FROM aa;
--you can skip the WITH altogether
UPDATE aa SET (a1,a2)=(SELECT bb.a1+2, bb.a2+1 FROM aa AS bb WHERE aa._id=bb._id)
WHERE _id<=2;
--soo now it should be (1,14,22)->(1,16,23) and (2,-6,-18)->(2,-4,-17), and it is
SELECT * FROM aa;
Hopefully sqlite is smart enough to not query incrementally but according to the documentation it is. When setting multiple columns using one select (case 2 and 3) a not valid id (no where _id in line) will give an error that can not be ignored using ON IGNORE, case 1 will set columns to null (for all ids >2) which is also bad.
You need to use an INSERT OR REPLACE statement, something like the following:
Assume maintable has 4 columns: key, col2, col3, col4
and you want to update col3 with the matching value from subtable
INSERT OR REPLACE INTO maintable
SELECT maintable.key, maintable.col2, subtable.value, maintable.col4
FROM maintable
JOIN subtable ON subtable.key = maintable.key

Resources