SQLite Compare two columns - sqlite

I am creating a database for my Psych class and I am scoring a personality profile. I need to compare two test items and, if they match a condition, then copy into a separate table.
Example (pseudocode is between \)Sqlite3
INSERT INTO Scale
SELECT* FROM Questions
WHERE \\if Question 1 IS 'TRUE' AND Question 3 IS 'FALSE' THEN Copy this Question
and its response into the Scale table\\;
I have about 100 other questions that work like this. Sample format goes like this:
IF FirstQuestion IS value AND SecondQuestion IS value THEN
Copy both questions into the Scale TABLE.
---------- EDITED AFTER FIRST RESPONSE! EDITS FOLLOW-------------
Here is my TestItems table:
ItemID | ItemQuestion | ItemResponse
```````````````````````````````````````````````````
1 | Is the sky blue? | TRUE
2 | Are you a person? | TRUE
3 | 2 Plus 2 Equals Five | FALSE
What I want to do: If Question 1 is TRUE AND Question 3 is FALSE, then insert BOTH questions into the table 'Scale' (which is setup like TestItems). I tried this:
INSERT INTO Scale
SELECT * FROM TestItems
WHERE ((ItemID=1) AND (ItemResponse='TRUE'))
AND ((ItemID=3) AND (ItemResponse='FALSE'));
HOWEVER: The above INSERT copies neither.
The Resulting 'Scale' table should look like this:
ItemID | ItemQuestion | ItemResponse
```````````````````````````````````````````````````
1 | Is the sky blue? | TRUE
3 | 2 Plus 2 Equals Five | FALSE

There is nothing wrong with your query. You're just there:
INSERT INTO Scale
SELECT * FROM Questions
WHERE `Question 1` = 1 AND `Question 3` = 0;
Here 1 and 0 are values (in your first case, true and false). First of all you should ensure there are fields Question 1 and Question 3 in your Questions table. Secondly the column count as well as data types of Scale table should match Questions table. Otherwise you will have to do selectively choose the fields in your SELECT query.
Edit: To respond to your edit, I am not seeing an elegant solution. You could do this:
INSERT INTO Scale
SELECT * FROM TestItems WHERE ItemID = 1 AND ItemResponse = 'TRUE'
UNION
SELECT * FROM TestItems WHERE ItemID = 3 AND ItemResponse = 'FALSE'
WHERE (SELECT COUNT(*) FROM (
SELECT 1 FROM TestItems WHERE ItemID = 1 AND ItemResponse = 'TRUE'
UNION
SELECT * FROM TestItems WHERE ItemID = 3 AND ItemResponse = 'FALSE'
) AS t) >= 2
Your insert did not work because ItemID cant be both 1 and 3 at the same time. My solution gets the required records to be inserted into Scale table, but verifies both the record exists by checking the count. Additionally you could (should) do as below since this can be marginally more efficient (the above SQL was to clearly show the logic being used):
INSERT INTO Scale
SELECT * FROM TestItems WHERE ItemID = 1 AND ItemResponse = 'TRUE'
UNION
SELECT * FROM TestItems WHERE ItemID = 3 AND ItemResponse = 'FALSE'
WHERE (
SELECT COUNT(*)
FROM TestItems
WHERE ItemID = 1 AND ItemResponse = 'TRUE'
OR ItemID = 3 AND ItemResponse = 'FALSE'
) >= 2

Related

Update the missing incremental number in a data set

In one of my tables I have a column called 'Priority' which is a number starting from 1. With new data the next number will be added to the new record.
When I deleted a data from the middle there will be a gap in priority. I want to run a plsql update statement so that the mising numbers will be replaced according the priority order that I had before.
original data
Priority | user
1 | A
2 | B
3 | C
4 | D
Then I delete the record B
Priority | user
1 | A
3 | C
4 | D
After Update it should be like
Priority | user
1 | A
2 | C
3 | D
You can do this without PL/SQL:
create table demo
( priority number, username varchar2(20) );
insert all
into demo values (2, 'A')
into demo values (4, 'B')
into demo values (9, 'C')
into demo values (10, 'D')
select * from dual;
merge into demo o
using ( select row_number() over (order by priority) as new_priority
, rowid as row_id
from demo ) n
on (n.row_id = o.rowid)
when matched then update set o.priority = n.new_priority;
select * from demo;
PRIORITY USERNAME
---------- --------------------
1 A
2 B
3 C
4 D

How to recursively calculate tree depth in SQLite

I currently have a table in SQLite that looks something like the following, forming a tree-like structure:
+-----+-----------+---------------+
| _id | parent_id | tree_depth |
+=====+===========+===============+
| 1 | 0 | 0 |
| 2 | 1 | (should be 1) |
| 3 | 2 | (should be 2) |
+-----+-----------+---------------+
I have very limited SQLite experience and the table is quite large, so I would hate to have to fill it out manually. Is there a query I could use to update the tree_depth column such that it properly represents the depth of the tree at that node? I tried selecting the parent's tree depth and incrementing, but for some reason it set everything to 1.
Any advise would be greatly appreciated.
EDIT: The query that I'm trying is:
UPDATE table SET tree_depth = (SELECT p.tree_depth FROM table JOIN table p ON p._id=table.parent_id) +1
You need a recursive CTE to compute the tree depth of each entry.
You can then use this data to look up the value to UPDATE:
WITH RECURSIVE depths(id, depth) AS (
SELECT _id, 0
FROM MyTable
WHERE parent_id = 0
UNION ALL
SELECT MyTable._id, depths.depth + 1
FROM MyTable
JOIN depths ON MyTable.parent_id = depths.id
)
UPDATE MyTable
SET tree_depth = (SELECT depth
FROM depths
WHERE depths.id = MyTable._id);
(Note: older Android versions do not support CTEs.)
Please try?
update table
set a.tree_depth = b.parent_id
If it doesn't wok, try to add this too:
FROM table a
INNER JOIN table b
on a._id= b._id

Select from table based on count

I have table like this:
id | name | type
-----------------
0 | firs | 2
1 | secs | 3
2 | this | 9
1 | thus | 3
I know id (it is not unique id) and type and I want to select records only if there is specified number of records with that id and type.
For one record I tried for example this:
select * from myTable
where
(select count(*) from myTable where myTable.id = 0 and myTable.type = 2) = 1;
This returns me all rows, not just the one row I want. Can anyone please tell me, what is the right way how to get the right result?
Your where statement looks like WHERE 1 = 1 and it's always true so SELECT gets every column from your table.
If you want select for example data where id = 1 and type = 3 and number of occurrences = 2 you can do something like that:
select * from myTable
where
(select count(*) from myTable where myTable.id = 1 and myTable.type = 3) = 2 and myTable.id = 1 and myTable.type = 3;

Select Case, when no data return

it is possible do a SELECT CASE, decode, nvl or another query function when I need verify if the return of a select query is empty or has a value?
For example, I have this:
Record | type | id_customer
-------+--------+-------------
1 | T | cus1
2 | A | cus2
3 | T | cus3
4 | | cus4
If I do this:
select decode(type,'T','Main','A','Adicional','none') from table where record=1;
I get Main.
If I fo this:
select decode(type,'T','Main','A','Adicional','none') from table where record=4;
I get none.
But if I do this:
select decode(type,'T','Main','A','Aditional','none') from table where record=5;
I get nothing, and is logic. So, I need get the decode value when the row exist and a text if the rows no exist.
So, I tried with SELECT CASE but is not posible get a value using COUNT. For example like this:
SELECT
CASE
WHEN count(1)>0 THEN decode(type,'T','Main','A','Aditional','none')
ELSE '-'
END
FROM TABLE WHERE record=5;
And get a ' - ', or the same if the record is 2, get 'Aditional'
Thanks a lot.
You can use aggregate functions min or max outside expression:
select max(decode(type,'T','Main','A','Aditional','none'))
from table
where record=5;
If query returns one row, you get value of that row. If query returns 0 rows, you get NULL.
Then you can replace NULL using nvl:
select nvl(max(decode(type,'T','Main','A','Aditional','none')), ' - ')
from table
where record=5;
EDIT
Also, if you need to choose one string from several:
select decode(max(decode(type,'T', 2, 'A', 1, 0)), 0, 'none', 1, 'Additional', 2, 'Main', null, ' - ')
from table
where record=5;
This is an option:
select decode(type,'T','Main','A','Aditional','none')
from table
where record = 5
union all
select '-'
from dual
where not exists (select 1 from table where record = 5);
It selects records with record = 5 and unifies them with '-', if no records exits with record = 5. Check out this Fiddle.

SQLite subquery: "IN" the result of the outer query

I have two tables user and pair. I want to get the number of duplicate pairs (a, b) for each user.name.
user
name | id
-------------
"Alice" | 0
"Bob" | 1
"Alice" | 2
pair
id | a | b
-----------
0 | 0 | 1
0 | 1 | 3
1 | 0 | 1
2 | 1 | 3
In the above example, the result should be:
name | id | c
-------------------
"Alice" | 0,2 | 1
"Bob" | 1 | 0
When there is only one id for each user, I can do this:
SELECT name, id, (
SELECT COUNT(*) FROM pair JOIN pair AS p USING (id, a, b)
WHERE id = user.id AND pair.rowid < p.rowid
) AS c FROM user;
When there is multiple ids, I can get the correct result from the below query, but it is quite slow when there is more rows and more subqueries.
SELECT name, GROUP_CONCAT(id), (
WITH t AS (SELECT id FROM user AS u WHERE name = user.name)
SELECT COUNT(*) FROM pair JOIN pair AS p USING (a, b)
WHERE pair.id IN t AND p.id IN t AND pair.rowid < p.rowid
) AS c FROM user GROUP BY name;
I want to know that is there a simple and efficient way for this, like changing the WHERE clause from pair.id = user.id to pair.id IN <<the user.id list>>?
/* This will not work! "Error: no such table: user.id" */
SELECT name, GROUP_CONCAT(id), (
SELECT COUNT(*) FROM pair JOIN pair AS p USING (a, b)
WHERE pair.id IN user.id AND p.id IN user.id AND pair.rowid < p.rowid
) AS c FROM user GROUP BY name;
The GROUP BY name operation can be sped up if the database is able to go through the rows in order, without having to sort the table.
This can be done with an index on the name column (the other column makes this a covering index, which helps only a little more):
CREATE INDEX user_name_id_index ON user(name, id);
The query looks up pair rows by their id, a, and b values; these lookups can be sped up with an index on these columns:
CREATE INDEX pair_id_a_b_index ON pair(id, a, b);
To help the query optimizer make better decisions when selecting indexes, run ANALYZE.
The query optimizer gets improved constantly; get the newest SQLite version, if possible.
To check how your queries are executed, look at the output of the EXPLAIIN QUERY PLAN command.

Resources