SQLite - Join returning duplicate result - sqlite

I've read through dozens of posts on this so apologies if it's easily answered but I'm just not getting it.
Table Name: users
user_id group_id house_name
==========================================
923828395 1 Alpha
722161580 2 Beta
923828395 1 Gamma
Users can be in multiple Groups, and in a different House per Group.
Table Name: points
user_id group_id points term_id
=====================================================================================
722161580 1 18 02078e51
923828395 1 11 02078e51
923828395 2 140 81450fc1
Users can accumulate points in each Group they reside in. In the data above, user_id 923828395 exists in both group_id 1 and 2, accumulating points in both.
Query:
SELECT users.user_id, users.house_name, points.points, points.group_id, points.term_id
FROM users
JOIN points ON points.user_id = users.user_id
WHERE points.term_id = "02078e51" AND points.group_id = "1"
I'm trying to get this to just return 2 rows. However it's returning:
users.user_id users.house_name points.points points.group_id points.term_id
722161580 Beta 18 1 02078e51
923828395 Alpha 11 1 02078e51
923828395 Gamma 11 1 02078e51
I think this is because I've got something wrong with my WHERE .. I've tried flipping that to just an AND statement but I get the same results.

You should join the tables on group_id also:
SELECT u.user_id, u.house_name, p.points, p.group_id, p.term_id
FROM users u JOIN points p
ON p.user_id = u.user_id AND p.group_id = u.group_id
WHERE p.term_id = '02078e51' AND p.group_id = '1'
See the demo.

Related

SQLITE get next row after ORDERBY

I need to get the next row from an ORDERBY query
I have 2 columns, ID(Primary key), Age(float) in a table T and I need something like the following
SELECT ID FROM T WHERE !> (inputted ID) + 1 rowID/Next row <! ORDERBY Age (then primary key, but I suspect if the Age values are the same SQLite would default to order by primary key anyway) LIMIT 1
Essentially it would select the next row after the inputted ID in the ordered table, its the next row / rowID + 1 I am not sure how to get.
As suggested here is a data set as an example
https://dbfiddle.uk?rdbms=sqlite_3.27&fiddle=19685ac20cc42041a59d318a01a2010f
ID Age
1 12.2
2 36.8
3 22.5
4 41
5 16.7
I am attempting to get the the following row from the ordered (by age) list given a specific ID
ID Age
1 12.2
5 16.7
3 22.5
2 36.8
4 41
Something similar to
SELECT ID FROM OrderedInfo WHERE ID = 5 ORDER BY Age ASC LIMIT 1 OFFSET 1;
My expected result would be '3' from the example data above
I have expanded the data set to include duplicate entries as I didn't implicitly state it could have such data - as such forpas answer works for the first example with no duplicate entries - thanks for your help
https://dbfiddle.uk?rdbms=sqlite_3.27&fiddle=f13d7f5a44ba414784547d9bbdf4997e
Use a subquery for the ID that you want in the WHERE clause:
SELECT *
FROM OrderedInfo
WHERE Age > (SELECT Age FROM OrderedInfo WHERE ID = 5)
ORDER BY Age LIMIT 1;
See the demo.
If there are duplicate values in the column Age use a CTE that returns the row that you want and join it to the table so that you expand the conditions:
WITH cte AS (SELECT ID, Age FROM OrderedInfo WHERE ID = 5)
SELECT o.*
FROM OrderedInfo o INNER JOIN cte c
ON o.Age > c.Age OR (o.Age = c.Age AND o.ID > c.ID)
ORDER BY o.Age, o.ID LIMIT 1;
See the demo.

Why does COUNT return NULL instead of `0` in this query?

I have the query
select d.did, count ( h.did ), unique_interested
from dealer as d
left outer join house as h
on h.did = d.did
left outer join (
-- cid = customer id
select hid, count (cid) as unique_interested
from is_interested
group by hid
) as ok
on h.hid = ok.hid
group by d.did
order by d.did asc
;
which is supposed to select the number of houses that each dealer is dealing, and the number of unique customers interested in said houses (as in the number of customers per dealer). This should happen even if the dealers have no houses to deal at the moment, which is why I'm using left outer joins when constructing the table the columns will be picked from.
Now, running this query against my database produces the following output:
d.did count ( h.did) unique_interested
----- -------------- ----------------
1 3
2 3 1
3 0
As you can see, instead of printing 0 in the last column, count returns null, when there is a null in one of the aparments produced by the last part of the join (as in cid is null):
select hid, count ( cid ) as unique_interested
from is_interested
group by hid
I know this is because there are apartments in the table produced by from, that no-one is interested in. But shouldn't count produce 0 instead of the actual column value null in every case?
Any explanation as to why this is happening would be appreciated, as it would lead me towards an answer to another question, which is "Why am I not getting the right number of unique interested customers per dealer from the table is_interested?", as with the current state of my database, the output should look more like:
d.did count ( h.did) unique_interested
----- -------------- ----------------
1 3 2
2 3 2
3 0 0

Creating even ranges based on values in an oracle table

I have a big table which is 100k rows in size and the PRIMARY KEY is of the datatype NUMBER. The way data is populated in this column is using a random number generator.
So my question is, can there be a possibility to have a SQL query that can help me with getting partition the table evenly with the range of values. Eg: If my column value is like this:
1
2
3
4
5
6
7
8
9
10
And I would like this to be broken into three partitions, then I would expect an output like this:
Range 1 1-3
Range 2 4-7
Range 3 8-10
It sounds like you want the WIDTH_BUCKET() function. Find out more.
This query will give you the start and end range for a table of 1250 rows split into 20 buckets based on id:
with bkt as (
select id
, width_bucket(id, 1, 1251, 20) as id_bucket
from t23
)
select id_bucket
, min(id) as bkt_start
, max(id) as bkt_end
, count(*)
from bkt
group by id_bucket
order by 1
;
The two middle parameters specify min and max values; the last parameter specifies the number of buckets. The output is the rows between the minimum and maximum bows split as evenly as possible into the specified number of buckets. Be careful with the min and max parameters; I've found poorly chosen bounds can have an odd effect on the split.
This solution works without width_bucket function. While it is more verbose and certainly less efficient it will split the data as evenly as possible, even if some ID values are missing.
CREATE TABLE t AS
SELECT rownum AS id
FROM dual
CONNECT BY level <= 10;
WITH
data AS (
SELECT id, rownum as row_num
FROM t
),
total AS (
SELECT count(*) AS total_rows
FROM data
),
parts AS (
SELECT rownum as part_no, total.total_rows, total.total_rows / 3 as part_rows
FROM dual, total
CONNECT BY level <= 3
),
bounds AS (
SELECT parts.part_no,
parts.total_rows,
parts.part_rows,
COALESCE(LAG(data.row_num) OVER (ORDER BY parts.part_no) + 1, 1) AS start_row_num,
data.row_num AS end_row_num
FROM data
JOIN parts
ON data.row_num = ROUND(parts.part_no * parts.part_rows, 0)
)
SELECT bounds.part_no, d1.ID AS start_id, d2.ID AS end_id
FROM bounds
JOIN data d1
ON d1.row_num = bounds.start_row_num
JOIN data d2
ON d2.row_num = bounds.end_row_num
ORDER BY bounds.part_no;
PART_NO START_ID END_ID
---------- ---------- ----------
1 1 3
2 4 7
3 8 10

SQLite Query to group continuous range of numbers into different grouping sets

I want get the islands of this table below:
Group MemberNo
A 100
A 101
A 200
A 201
A 202
A 203
X 100
X 101
A 204
X 301
X 302
A 500
A 600
I want get this results using SQL (the islands):
Group FromMemberNo ToMemberNo
A 100 101
A 200 204
X 100 101
X 301 302
A 500 500
A 600 600
I have seen a lot of codes/forums for this but not working with SQLite because SQLite doesn't have CTEs.
100-101 is continuous so that it will be group into one.
Does anyone know how to do it in SQLite?
The fastest way to do this would be to go through the ordered records of this table in a loop and collect the islands manually.
In pure SQL (as a set-oriented language), this is not so easy.
First, we find out which records are the first in an island. The first record does not have a previous record, i.e., a record with the same group but with a MemberNo one smaller:
SELECT "Group",
MemberNo AS FromMemberNo
FROM ThisTable AS t1
WHERE NOT EXISTS (SELECT 1
FROM ThisTable AS t2
WHERE t2."Group" = t1."Group"
AND t2.MemberNo = t1.MemberNo - 1)
To find the last record of an island, we have to find the record with the largest MemberNo that still belongs to the same island, i.e., has the same group, and where all MemberNos in the island are continuous.
We detect continuous MemberNos by computing the difference between their values in the first and last records.
The last MemberNo of the island with group G and first MemberNo M can be computed like this:
SELECT MAX(MemberNo) AS LastMemberNo
FROM ThisTable AS t3
WHERE t3."Group" = G
AND t3.MemberNo - M + 1 = (SELECT COUNT(*)
FROM ThisTable AS t4
WHERE t4."Group" = G
AND t4.MemberNo BETWEEN M AND t3.MemberNo)
Finally, plug this into the first query:
SELECT "Group",
MemberNo AS FromMemberNo,
(SELECT MAX(MemberNo)
FROM ThisTable AS t3
WHERE t3."Group" = t1."Group"
AND t3.MemberNo - t1.MemberNo + 1 = (SELECT COUNT(*)
FROM ThisTable AS t4
WHERE t4."Group" = t1."Group"
AND t4.MemberNo BETWEEN t1.MemberNo AND t3.MemberNo)
) AS LastMemberNo
FROM ThisTable AS t1
WHERE NOT EXISTS (SELECT 1
FROM ThisTable AS t2
WHERE t2."Group" = t1."Group"
AND t2.MemberNo = t1.MemberNo - 1)

combining IDs under GROUP BY?

I have an sqlite table "log" that looks like this:
ID p_id viewer
----------------------
1 1 100
2 1 200
3 1 300
4 3 550
5 3 230
6 5 420
7 2 320
8 2 203
9 9 10
10 9 55
And I want to get the average viewers from each p_id. That'd be
SELECT avg(viewer) FROM log GROUP BY p_id
But I want to treat p_id 1 and 5 as the same, so I'd get the average viewers of p_id 1 and 5 combined. How do I do that? Note that the table is much larger, and I need to treat two p_id's as the same multiple times. Can I still do a "group by" to achieve this, or are there other ways?
I tried this and it works with mySQL at least.
SELECT avg(viewer) FROM log GROUP BY CASE p_id WHEN 5 THEN 1 ELSE p_id END;
Edit: When using an alias-table named 'aliases' with the fields 'alias_from_id' and 'aliased_as':
SELECT
CASE ISNULL((SELECT aliased_as FROM aliases WHERE (alias_from_id = log.pid)))
WHEN 1 THEN log.pid ELSE aliased_as END AS the_id,
SUM(value) AS value_sum
FROM log
LEFT JOIN aliases ON (alias_from_id = pid)
GROUP BY
CASE ISNULL((SELECT aliased_as FROM aliases WHERE (alias_from_id = log.pid)))
WHEN 1 THEN log.pid ELSE aliased_as END
I've tested this with mySQL and it works like a charm. It might be possible to simplify this SQL-query a bit but this is the best I can do at the moment :)
Edit2: Changed ISNULL to the corresponding SQLite IFNULL function
SELECT
IFNULL((SELECT aliased_as FROM aliases WHERE (alias_from_id = log.pid)), log.pid) AS the_id,
SUM(value) AS value_sum
FROM log
LEFT JOIN aliases ON (alias_from_id = pid)
GROUP BY
IFNULL((SELECT aliased_as FROM aliases WHERE (alias_from_id = log.pid)), log.pid)
I don't know if SQLite supports the AS keyword, if it doesn't then just remove that keyword - the functionality should be the same.

Resources