sqlite query - select data which prefers some value - sql

I'm trying to create an sqlite query but I'm having some problems.
Let's say that table has three columns id, foreign-id and value.
I need to select all rows with distinct foreign_id with a given value, however that value may not exist for all different foreign_ids.
In which case a row where value is set to some fallback value must be selected (such row always exists) for that foreign_id.
I apologize for my english since I'm not native english speaker.
Here is an example:
Table:
id | foreign_id | value
------------------------
1 | 1 | 1
2 | 1 | 2
3 | 1 | 3
4 | 2 | 1
5 | 2 | 3
If desired value is 2 and fallback value is 1 then the query should return
id | foreign_id | value|
------------------------
2 | 1 | 2
4 | 2 | 1
It return row with id 1 because it has desired value 2 for foreign_id 1.
And it return row with id 4 because for foreign_id 2 a row with value of 2 does not exits, so it selects a row with fallback value of 1.
Hope that clears up my question a bit.

You might be able to do it with a Union... something like:
SELECT DISTINCT (foreign_id), value
FROM TABLE
WHERE value = 2
UNION
SELECT DISTINCT (foreign_id), '1' as value
FROM TABLE
WHERE foreign_id NOT IN (
SELECT DISTINCT (foreign_id), value
FROM TABLE
WHERE value = 2
)
where everything that has a value 2 set value as 2 and everything else sets value as 1
(I haven't tested this query, you might have to do some tweaking)

This is the solution that I produced based on Seth's answer.
SELECT DISTINCT (foreign_id), value, id FROM testTable
WHERE value = 2
UNION SELECT DISTINCT (foreign_id), value, id FROM testTable
WHERE value = 1
AND foreign_id NOT IN
(
SELECT foreign_id
FROM testTable
WHERE value = 2
)

Related

Can I join each row of table1 to a unique row of table 2?

I was hoping to query in all the rows of a table that has its ids starting at some number, and update each row of the original table with a one to one of the second table.
For example:
normal
id | fk_test_id
----------------
1 | null
2 | null
3 | null
starts_after
id |
----
12 |
13 |
14 |
What UPDATE can I use to make normal look like this:
id | fk_test_id
----------------
1 | 12
2 | 13
3 | 14
I tried:
UPDATE normal SET fk_test_id = starts_after.id FROM starts_after; which just joins on the first row of starts_after.
UPDATE normal SET fk_test_id = (SELECT id FROM starts_after ORDER BY random() LIMIT 1); Where the subquery only executes once.
Filtering the subquery by which fk_test_ids are already chosen, but it only executes on the pre-updated data.
If you added record with specific order in to starts_after you can use below query:
update normal n
set fk_test_id = tmp.id
from (select id,
row_number() over (order by id)
from starts_after) tmp
where tmp.row_number = n.id;
I ordered by id from starts_after table (ASC) and create range of record with row num:
id | row_number
----------------
12 | 1
13 | 2
14 | 3
After that i join two table and update records

pulling data from max field

I have a table structure with columns similar to the following:
ID | line | value
1 | 1 | 10
1 | 2 | 5
2 | 1 | 6
3 | 1 | 7
3 | 2 | 4
ideally, i'd like to pull the following:
ID | value
1 | 5
2 | 6
3 | 4
one solution would be to do something like the following:
select a.ID, a.value
from
myTable a
inner join (select id, max(line) as line from myTable group by id) b
on a.id = b.id and a.line = b.line
Given the size of the table and that this is just a part of a larger pull, I'd like to see if there's a more elegant / simpler way of pulling this directly.
This is a task for OLAP-functions:
select *
from myTable a
qualify
rank() -- assign a rank for each id
over (partition by id
order by line desc) = 1
Might return multiple rows per id if they share the same max line. If you want to return only one of them, add another column to the order by to make it unique or switch to row_number to get an indeterminate row.

How to get the count of similar columns from the same table

I have a table with columns id and value. I want to select the records where there exist other records in the same value with a lower id but the same value. I need the count of these. For example, if I have this table
id | value
---+------
1 | 1
2 | 2
3 | 1
4 | 3
5 | 2
6 | 1
I need the answer
id | value | count
---+-------+------
3 | 1 | 1 // 1 other row with value 1 and a lower id
5 | 2 | 1 // 1 other row with value 2 and a lower id
6 | 1 | 2 // 2 other rows with value 1 and a lower id.
I can get the first two columns by doing
select id as id1, value as value1 from table where exists
(select id as id2, value as value2 from table
where value2 = value1 and id1 < id2);
However I can't work out how to get the count. Should I use having or group by to get the count?
You can use row_number() for this:
select t.*
from (select t.*,
row_number() over (partition by value order by id) - 1 as prev_values
from t
) t
where prev_values > 0;

SQL advanced query counting the max value of a group

I want to create a query that will count the number of times the following condition is met.
I have a table that consists of multiple records with a matching foreign key. I want to check only for each of the foreign key groups if the highest value of another column of that key occurs more than once. If it does that will up the count.
Data
--------------------------
ID | Foreign Key | Value
--------------------------
1 | 1 | 1
2 | 1 | 2
3 | 1 | 2
4 | 2 | 0
5 | 2 | 2
6 | 2 | 1
7 | 3 | 0
8 | 3 | 1
9 | 3 | 1
The query I want should return the number 2. This is because the maximum value in group 1(Foreign Key) occurs twice, the value is 2. In group 2 the maximum value is 2 but only occurs once so this will not up the count. Then in group 3 the maximum value is 1 which occurs twice which will up the count. The count therefore ends up as two.
All credit goes to the comment from #Bob, but here is the sql that solved this problem.
SELECT Count(1)
FROM (SELECT DISTINCT foreign_key
FROM (SELECT foreign_key,
Count(1)
FROM data
WHERE ( foreign_key, value ) IN (SELECT foreign_key,
Max(value)
FROM data
GROUP BY foreign_key)
GROUP BY foreign_key
HAVING Count(1) > 1) AS data) AS data;
This is one approach:
select max(num_at_max)
from (select t.*, count(val) over(partition by fk) as num_at_max
from tbl t
join (select max(max_val_by_grp) as max_val_all_grps
from (select fk, max(val) as max_val_by_grp
from tbl
group by fk) x) x
on t.val = x.max_val_all_grps) x

SQL return a row when filter doesn't exist

Say I have a table that contains a column with a number in it, when I match that number in a where statement I want the value from some other column in that row if that some other column is empty. Easy enough
I also want listed however, when that number just plain doesn't exsist. IS NULL doesn't work since it's just plain not there
(row) id | num | text
1 1 | 5433 | a
2 1 | 1234 | b
3 3 | 4532 | b
4 3 | 1234 | c
5 4 | 5312 | d
6 4 | 1234 |
7 5 | 4654 | a
query...
select text
from table
where text IS NULL AND num=1234
Would return row 6, however I want it to also return id 5 since it doesn't not contain a 1234 value, like so
5 | 1234 |
Put a or there
select text
from table
where text IS NULL or num<>1234
I think for clarity sake, your best bet will be two separate queries unioned together. So you've already got your half, just union it to one that does what you want:
select text
from table
where text IS NULL AND num=1234
union
select text
from table
where id not in (select id from table where num = 1234)
Make sure that you are using an OR operator instead of AND, because text cannot be NULL and 1234 at the same time.
To test for empty strings, just use an empty double quote as well '':
select text
from table
where text IS NULL OR num = 1234 OR text = ''
Edit following user comment
I have made a further edit following your comment, that may (or may not) answer your question.
Essentially, I believe you are asking for the query to return rows with an empty\null text and with 1234 as the num, and also all rows that do not have 1234 as the num.
This should accomplish the result, as follows:
select text
from table
where (text IS NULL AND num = 1234) OR num != 1234
This will return results that have ANY of the following:
all rows with text set to NULL and a num of 1234
all rows with num not set to 1234
Based on your original row list from the above, you will get the following results with this query:
(row) id | num | text
1 1 | 5433 | a
3 3 | 4532 | b
5 4 | 5312 | d
6 4 | 1234 |
7 5 | 4654 | a
This will basically exclude all rows that have a num of 1234 and a text that is not NULL.
You want the rows where the text value of 1234 is NULL, whether the row exists or not. But, in the second case, you need to "create" the row.
The following approach splits this into a union all. The first part gets the rows where the value exists in the table but is NULL. The second "creates" a row when there is no 1234 row:
select id, 1234, text
from table t
where text is NULL and num = 1234
union all
select id, 1234, NULL as text
from table t
group by id
having sum(case when num = 1234 then 1 else 0 end) = 0;
You can do this with one expression, but it is a bit more complicated:
select id, 1234 as num, NULL as text
from table t
group by id
having sum(case when num = 1234 then 1 else 0 end) = 0 or
sum(case when num = 1234 and text is null then 1 else 0 end) = 1;
try this
select text,num
from table
where isnull(text,0)=0 or num=1234
Try this way:
select *
from tab t
where text is null
or
not exists
(
select 1
from tab t1
where t1.id = t.id
and t1.num =1234
)
Sql Fiddle Demo
or this way:
select t.ID,1234,null as text
from tab t
left join tab t1 on t1.id = t.id
and t1.num =1234
where t1.id is null
union all
select t.ID,t.num,t.Text
from tab t
where text is null
Sql Fiddle Demo