Select unique ordered values of several columns in sql - sql

I am using a table with a couple of geometries in each row. I would like that each geometries appears only once in my database. I sorted the couple by distance. I succeded to have distinct geom1 or geom2 but never in the same time. The ids are linked to their related geometries.
| id1 | id2 | distance| | id1 | id2 | distance|
| 1 | 2 | 3 | | 1 | 2 | 3 |
| 2 | 1 | 4 | -> | 2 | 1 | 7 |
| 2 | 2 | 7 |
| 1 | 1 | 9 |
My table contains more than 2 millions rows, so the performance is an issue.
I taught to create several temp table where I group by the id1 and then id2, collect the missing values and group by again and again... But if anyone has a better idea, It would be amazing.
Thanks,

if i understand correctly you are looking for distinct triplets of id1, id2 and distance:
SELECT DISTINCT id1, id2 , distance FROM <table name>;
or
SELECT id1, id2 FROM <table name> GROUP BY id1, id2, distance;

You seems want :
select t1.*
from table t1
where id2 = (select max(t1.id2) from table t2 where t2.id1 = t1.id1);

Related

How to order table by date column on 2 other joined tables in Laravel?

I have the following tables:
table_1
id | table_2_id | table_3_id | table_1_specific_columns
1 | null | 1 |
2 | 1 | null |
...
and:
table_2
id | table_2_specific_columns| date
1 | blabla | 01-01-1990
2 | bababa | 02-02-1992
...
and:
table_3
id | table_3_specific_columns| date
1 | blabla | 01-01-1991
2 | bababa | 02-02-1989
...
The database is PostgreSQL
How to order table_1 by date column on table_2 and table_3 joined tables in Laravel?
Can this be done using Eloquent?
Edit: the expected result, if it can be done using eloquent, is a Collection of table_1 model with table_2 and table_3 relations ordered by the date column in table_2 and table_3
Edit2:
Expected result for asc ordering of table_1. The date column is added for quick reference, not required.
id | table_2_id | table_3_id | date
1 | null | 2 | 02-02-1989
2 | 1 | null | 01-01-1990
1 | null | 1 | 01-01-1991
2 | 2 | null | 02-02-1992
From what you've shared you either have a table_2 or table_3 relationship (if any) so you could do:
DB::table('table_1')
->leftJoin('table_2', 'table_1.table_2_id','table_2.id')
->leftJoin('table_3', 'table_1.table_3_id','table_3.id')
->select('table_1.*')
->orderByRaw('COALESCE(table_2.date, table_3.date)');
Note: If a row relates to both table 2 and table 3 the order is given by table_2 only
The downside here is that you'll also include any rows of table_1 which don't relate to anything. You could omit those by adding ->whereNotNull('table_2.id')->orWhereNotNull('table_3.id')
If you have a model you can modify the above to use e.g. Table1::leftJoin.... instead of using DB::table in this case select('table_1.*') becomes even more important to not add the wrong values in the model objects
Hi you can use syntax
orderByRaw
So your code will be similar to :
Table::orderByRaw("column DESC, column2 ASC")->get();

Replicate rows based on one to many relationships between columns

This has to be a solved problem but I don't know the right terms to search for on google. So, will explain the problem here.
I have the following dataset that has two different identifiers for users (say id1 and id2).
+------+-----+-------+
| id1 | id2 | value |
+------+-----+-------+
| 1 | 11 | blah1 |
| 1 | 12 | blah2 |
| 2 | 13 | blah3 |
| null | 14 | blah4 |
+------+-----+-------+
There is a one-to-many relationship between id1 and id2 and so users with id2 11 and 12 are actually the same users. I want to replicate the rows for such users so that the value is associated with each id2. The resulting dataset would then look like
+------+-----+-------+
| id1 | id2 | value |
+------+-----+-------+
| 1 | 11 | blah1 |
| 1 | 12 | blah2 |
| 2 | 13 | blah3 |
| null | 14 | blah4 |
| 1 | 12 | blah1 |
| 1 | 11 | blah2 |
+------+-----+-------+
As you can see, the value blah1 is now associated with both 11 and 12 id2, as is the value blah2.
There must be some kind of self-join that does that but I am not aware of what it is called (SQL newbie). Would appreciate if some one could point me in the right direction.
Well, you can self join, its totally permitted...
Join will link columns based on a key connection (in the general case)
Notice that in this case will also need union, because you'd like more lines, not columns
SELECT t.*
FROM
table t
INNER JOIN table t2 ON t.id1 = t2.id1 AND t. id2 != t2.id2
UNION
SELECT t.*
FROM
table t
INNER JOIN table t2 ON t.id1 = t2.id1 AND t. id2 = t2.id2
You can generate the rows using join for this purpose:
select i.id1, i.id2, iv.value
from (select distinct id1, value from t) iv join
(select distinct id1, id2 from t) i
on iv.id1 = i.id1 ;
Actually, the second select distinct is probably not necessary (unless your original data has duplicates which is would if you added these rows back into the table), but I think it make the query clearer. This should also work:
select t.id1, t.id2, iv.value
from (select distinct id1, value from t) iv join
t
on iv.id1 = t.id1 ;

How to pivot a table in oracle SQL that has no feature to use as columns?

I have a query that looks like this
select
parentid,
id
from
table
order by
parentid;
The parentid is a reference to another type of object in a different table. The records in this table are additional information about the record in the parent table, and there can be anywhere from 1 to 10 ids associated with a parent id. The records don't have any particular order, either. So right now, the query above returns something like this:
parentid | id
---------------------------
1 10
1 20
1 30
1 40
2 50
2 60
3 70
4 80
4 90
4 100
I'd like to transform the results into a table like this
parentid | id1 | id2 | id3 | id4 ....
--------------------------------------------------------------------
1 10 20 30 40
2 50 60
3 70
4 80 90 100
I don't really care what column the ids end up in, since there's no order, but I do want each of them to be assigned to some column associated with the parent id. I thought about using pivot, but the examples I have seen make it look like you have to have an ordering or some other unique identifier associated with the ids to transform them into columns. There's no such field that could order or otherwise distinguish these records from one another. Is there a way to pivot without this, or to randomly assign some attribute that I could then use to pivot on?
Also, not sure if it will matter to the answer, but the table above is also a trivialization of the actual data for the sake of clarity - in reality there's tens of thousands of parent ids and records in this table.
Just create your column:
SqlFiddleDemo
SELECT 'ID' || ROW_NUMBER() OVER (PARTITION BY "parentid" ORDER BY "id") AS rn,
"parentid",
"id"
FROM Table1
OUTPUT
| RN | parentid | id |
|-----|----------|-----|
| ID1 | 1 | 10 |
| ID2 | 1 | 20 |
| ID3 | 1 | 30 |
| ID4 | 1 | 40 |
| ID1 | 2 | 50 |
| ID2 | 2 | 60 |
| ID1 | 3 | 70 |
| ID1 | 4 | 80 |
| ID2 | 4 | 90 |
| ID3 | 4 | 100 |
Or use this version if have more than 9 columns
SELECT 'ID' || LPAD(rn, 2, '0') as rn,
"parentid",
"id"
FROM (
SELECT ROW_NUMBER() OVER (PARTITION BY "parentid" ORDER BY "id") AS rn,
"parentid",
"id"
FROM Table1
) T

PostgreSQL Table Overlap Count

I am using postgresql.
I have a table that looks like this
| id1 | id2 |
------------------------------------
| 1 | 6 |
| 1 | 12 |
| 2 | 6 |
| 3 | 1 |
| 3 | 2 |
| 2 | 2 |
I am trying to design a query that given for example: id1=1, it will return all id1's with their overlap in id2 in relation to the given id1. Do not include the given id1 in the results.
For example, if it were given id1=1, the result should be:
| id1 | num_occurences |
------------------------------------
| 2 | 1 |
| 3 | 0 |
An id1 of 2 would return 1 because id1=1 and id1=2 have only id2=6 in common. id1 of 3 returns 0 because there is no overlap in occurrences.
I think I might want to use an INNER JOIN but I am not sure.
Any suggestions?
Since you also want zero results, you could use a LEFT JOIN to check the condition;
SELECT a.id1, COUNT(b.id1) num_occurences
FROM mytable a
LEFT JOIN mytable b ON a.id2 = b.id2 AND b.id1 = [id]
WHERE a.id1 <> [id]
GROUP BY a.id1
...where in your case, [id]=1.
What it does is check for each row in "b" (with id1=1) check if there's a row in "a" with the same id2 and an id1 <> 1. Then all it needs to do is group and count the results.
An SQLfiddle to test with.
SELECT id1, SUM( CASE
WHEN id1=id2 THEN 1
ELSE 0
END )
AS num_occurences
FROM table
GROUP by id1
Not a single JOIN was given that day.

Sqlite: Select last row group by 2 column

I'm trying to get the last row of my table but with 2 column.
+----+-----+---------+
| id1| id2 | info |
+----+-----+---------+
| 1 | 2 | info |
| 2 | 1 | NULL |
| 2 | 3 | info |
| 2 | 1 | NULL |
+----+-----+---------+
I tried:
SELECT * FROM table GROUP BY id1
but I got:
1 2
2 3
2 1
What I need:
2 3
2 1
In other words, I need the last row of each couple ids
Any idea?
SELECT DISTINCT id1, id2 FROM table WHERE id1=2
This should do the trick. Unless you want to apply an aggregation function to other columns, SELECT DISTINCT should to the trick. It will drop any duplicate rows.
If you want to get all items with the highest value dynamically, you can use:
SELECT DISTINCT id1, id2 FROM table WHERE id1=(SELECT MAX(id1))