Multiple "SELECT DISTINCT" query - mysql

I have tried several methods to select multiple columns in a table for unique or distinct data from a table, including queries like:
SELECT
(SELECT GROUP_CONCAT(DISTINCT a) FROM TableName),
(SELECT GROUP_CONCAT(DISTINCT b) FROM TableName),
(SELECT GROUP_CONCAT(DISTINCT c) FROM TableName);
SELECT(a, b, c) FROM TableNamegroup by 'a' order by a asc;
SELECT DISTINCT a FROM TableName
UNION
SELECT DISTINCT b FROM TableName
UNION
SELECT DISTINCT c FROM TableName;
But they either don't work or return the information in a format that I can't use. What I need is a format like this:
+--------------------+
| a | b | c |
|--------------------|
| 1 | 1 | 1 |
| 2 | 2 | 2 |
| 3 | 3 | 3 |
etc......
Short of doing individual queries, is there a way to do this?

If you need three columns, then your select needs three columns. If you want unique combinations:
select distinct a, b, c
from TableName;
Is this what you want?
I suspect that you want lists of the unique ids in three columns. You can do this using variables in MySQL:
select rn, max(a) as a, max(b) as b, max(c) as c
from ((select #rna := #rna + 1 as rn, a, null as b, null as c
from (select distinct a from TableName) t cross join
(select #rna := 0) const
) union all
(select #rnb := #rnb + 1 as rn, null, b, null
from (select distinct b from TableName) t cross join
(select #rnb := 0) const
) union all
(select #rnc := #rnc + 1 as rn, null, null, c
from (select distinct c from TableName) t cross join
(select #rnc := 0) const
group by c
)
) abc
group by rn
order by rn;
Here is an example of it working in SQL Fiddle.

Related

MySQL join on row number (first with first, second with second etc)

Let's say I have 2 simple tables
Table t1 Table t2
+------+ +------+
| i | | j |
+------+ +------+
| 42 | | a |
| 1 | | b |
| 5 | | c |
+------+ +------+
How can I have an output of the 2 tables, joined without any condition except the row number?
I would like to avoid the creation of another index if possible.
I am using MySQL 5.7
With this example, the output would be :
Table output
+------+------+
| i | j |
+------+------+
| 42 | a |
| 1 | b |
| 5 | c |
+------+------+
What you ask can be done, assuming that your comment is true;
"Even if table i and j are subqueries (containing order by)?"
Schema (MySQL v5.7)
CREATE TABLE table_1 ( i INT );
CREATE TABLE table_2 ( j VARCHAR(4) );
INSERT INTO table_1
VALUES (3),(5),(1);
INSERT INTO table_2
VALUES ('c'), ('b'),('a');
Query
SELECT t1.i, t2.j
FROM (SELECT t1.i
, #rownum1 := #rownum1 + 1 AS rownum
FROM (SELECT table_1.i
FROM table_1
ORDER BY ?) t1
CROSS JOIN (SELECT #rownum1 := 0) v) t1
JOIN (SELECT t2.j
, #rownum2 := #rownum2 + 1 AS rownum
FROM (SELECT table_2.j
FROM table_2
ORDER BY ?) t2
CROSS JOIN (SELECT #rownum2 := 0) v) t2 ON t2.rownum = t1.rownum;
However, this approach is a) not efficient, and b) indicative of questionable design. You probably want to look for something that actually relates your two tables or, if nothing exists, create something. If there is really nothing that relates the two tables, then you'll have trouble with the ORDER BY clauses anyway.
If the tables do not necessarily have the same number of rows, then use union all and group by -- along with variables:
select max(t.i) as i, max(t.j) as j
from ((select (#rn1 := #rn1 + 1) as seqnum, t1.i
from t1 cross join
(select #rn1 := 0) params
) union all
(select (#rn2 := #rn2 + 1) as seqnum, t2.j
from t2 cross join
(select #rn2 := 0) params
)
) t
group by seqnum;
Note: The results in each column are in an arbitrary and indeterminate order. The order might vary on different runs on the query.
You don't provide enough information to ensure the ordering.
you can try this code
select t1.i,t2.j
from
(SELECT i,#row_num:=#row_num+1 as row_num FROM t1, (SELECT #row_num:= 0) AS sl) t1
join
(SELECT j,#row_num:=#row_num+1 as row_num FROM t2, (SELECT #row_num:= 0) AS sl) t2
on t1.row_num=t2.row_num

How to JOIN the same table with itself using ORDER BY RAND()?

I want to get random combinations of id of a table with itself.
SELECT id FROM t1
SELECT id as id2 FROM t1 ORDER BY RAND()
SELECT id as id3 FROM t1 ORDER BY RAND()
How can I JOIN these queries to get
SELECT id,id2,id3
1 random_id random_id
2 random_id random_id
3 random_id random_id
4 random_id random_id
5 random_id random_id
In other words, what can be the point of JOINing to simply place these three SELECTs side by side.
It is beneficial to create a unique combination, but with the above query ORDER BY RAND() can repeat the same id to id2 and id3. The former case is ideal, but the latter sufficiently works for me.
If you are using MySQL 8+, then ROW_NUMBER might work here:
WITH cte AS (
SELECT id,
ROW_NUMBER() OVER (ORDER BY id) rn1,
ROW_NUMBER() OVER (ORDER BY RAND(UNIX_TIMESTAMP())) rn2,
ROW_NUMBER() OVER (ORDER BY RAND(UNIX_TIMESTAMP()+1)) rn3
FROM t1
)
SELECT
t1.id,
t2.id AS id2,
t3.id AS id3
FROM cte t1
INNER JOIN cte t2
ON t1.rn1 = t2.rn2
INNER JOIN cte t3
ON t1.rn1 = t3.rn3;
The demo below was from a sample table containing the id values from 1 to 10 inclusive.
Demo
If you truly want random, then repeats are allowed. That suggests:
select t.*,
(select t2.id
from t t2
order by rand()
limit 1
) as id2,
(select t3.id
from t t3
order by rand()
limit 1
) as id3
from t;
If you want permutations in older versions of MySQL, then variables are handy:
select t.id, t1.id, t2.id
from (select t.id, (#rn := #rn + 1) as seqnum
from t cross join
(select #rn := 0) params
) t join
(select t.id, (#rn1 := #rn1 + 1) as seqnum
from (select t.* from t order by rand()) t cross join
(select #rn1 := 0) params
) t1
using (seqnum) join
(select t.id, (#rn2 := #rn2 + 1) as seqnum
from (select t.* from t order by rand()) t cross join
(select #rn2 := 0) params
) t2
using (seqnum);
In MySQL 8+, Tim's answer is the best approach.
Here is a db<>fiddle
For this sample data of sequential ids from 1 to n, this query that uses string functions will work and will return in the columns id2 and id3 all if the ids once:
select t1.id,
find_in_set(t1.id, t2.ids2) id2,
find_in_set(t1.id, t3.ids3) id3
from tablename t1
cross join (
select group_concat(id order by rand()) ids2
from tablename
) t2
cross join (
select group_concat(id order by rand()) ids3
from tablename
) t3
See the demo.
Results (like):
| id | id2 | id3 |
| --- | --- | --- |
| 1 | 5 | 4 |
| 2 | 2 | 5 |
| 3 | 1 | 2 |
| 4 | 4 | 3 |
| 5 | 3 | 1 |

How to select a column value depending if id is even or odd

having a table structure of id and a name:
create table Mytable (
id integer not null,
name varchar(30) not null,
unique(id)
);
insert into Mytable (id,name) values
(1 , 'one'),
(2 , 'two'),
(3 , 'three'),
(4 , 'four'),
(6 , 'six');
How may I get a mix of even and odd rows in a result table like:
even | odd
-----------
null one '0 is not in Mytable so it puts null value
two three
four null '5 and 6 are not in Mytable so it puts null value
six null
I was trying to first get the following as a template and use it later as
a dictionary:
SELECT MIN(id-1) as id,MAX(id-1) as col
FROM Mytable
GROUP BY FLOOR((id+1)/2);
I get:
id col
0 1
2 3
5 5
But I do not know how to continue
For MySQL Version <= 5.7, You can use the below query
Query 1:
SELECT
MAX(CASE WHEN m.id % 2 = 0 THEN name END) AS even,
MAX(CASE WHEN m.id % 2 = 1 THEN name END) AS odd
FROM
(
SELECT
(SELECT MAX(id) FROM mytable) AS maxid,
#rn := #rn + 1 AS rn,
(SELECT IF((#rn * 2) <= maxid, #rn, NULL)) AS rid
FROM
mytable
JOIN
(SELECT #rn := -1) AS var
) AS t
JOIN
mytable m ON FLOOR(m.id/2) = t.rid
GROUP BY rid;
Result 1:
even | odd
:--- | :----
null | one
two | three
four | null
six | null
Demo 1:
db fiddle
Query 2:
After confirmation based on #Madhur Bhaiya comment. If there is no row for id = 8 and 9 then it will show null, null.
SELECT
MAX(CASE WHEN m.id % 2 = 0 THEN name END) AS even,
MAX(CASE WHEN m.id % 2 = 1 THEN name END) AS odd
FROM
(
SELECT
(SELECT MAX(id) FROM mytable) AS maxid,
#rn := #rn + 1 AS rn,
(SELECT IF((#rn * 2) <= maxid, #rn, NULL)) AS rid
FROM
(SELECT 0 UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) t
JOIN
(SELECT 0 UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) t2
JOIN
(SELECT #rn := -1) var -- currently it will return 1..100, if needed more add joins based on your needs
) AS t
LEFT JOIN
mytable m ON FLOOR(m.id/2) = t.rid
GROUP BY rid HAVING rid IS NOT NULL;
Result 2:
even | odd
:------ | :-----
null | one
two | three
four | null
six | null
null | null
null | eleven
null | null
null | null
sixteen | null
Demo 2:
db fiddle
For MySQL Version > 8.0, You can use #Nick query but if you need null, null like Result 2 mentioned for <= v5.7 then add LEFT JOIN with ORDER BY clause.
Query:
with recursive maxid as (
select max(id) as id from Mytable)
, cte as (
select 0 as rid
union all
select rid + 1
from cte
cross join maxid
where (rid + 1) * 2 <= maxid.id)
select max(case when m.id % 2 = 0 then name end) as even,
max(case when m.id % 2 = 1 then name end) as odd
from cte
left join Mytable m on floor(m.id / 2) = cte.rid
group by rid order by rid;
Result:
even | odd
:------ | :-----
null | one
two | three
four | null
six | null
null | null
null | eleven
null | null
null | null
sixteen | null
Demo: db fiddle
Credits: Thanks to #Nick, #Madhur Bhaiya for the fiddle and the logic used to create this query.
Here's a CTE based query that will work in SQL Server and MySQL > v8.0 (with the addition of the keyword recursive before maxid). It generates a list of rows that encompasses the pairs of MyTable values (in the sample, this is 0,1,2,3) and then JOINs that to Mytable to extract the even/odd column values:
with maxid as (
select max(id) as id from Mytable)
, cte as (
select 0 as rid
union all
select rid + 1
from cte
cross join maxid
where (rid + 1) * 2 <= maxid.id)
select max(case when m.id % 2 = 0 then name end) as even,
max(case when m.id % 2 = 1 then name end) as odd
from cte
join Mytable m on m.id / 2 = cte.rid
group by rid
Output:
even odd
one
two three
four
six
Demo on dbfiddle

SQL query to join two tables with no repeated values?

Table 1
ID | NAME | WARD_ID|
1 A 1
2 B 1
3 C 2
4 D 2
Table 2
ID | MONTH1 | MONTH2 | WARD_ID|
1 9 10 1
2 6 11 1
3 5 12 2
4 13 14 2
I want to join this two table and produce the following output:
ID | NAME | MONTH1 | MONTH2 | WARD_ID|
1 A 9 10 1
2 B 6 11 1
3 C 5 12 2
4 D 13 14 2
In the ON condition of the query I have to keep WARD_ID equal for both the tables. I could not able to figure out the solution. Anyone have any experience with a query like this?
I think you want something like this:
select t1.*, t2.*
from (select t1.*,
(#rn1 := if(#w1 = ward_id, #rn1 + 1,
if#w1 := ward_id, 1, 1)
)
) as rn
from (select t1.* from table1 t1 order by ward_id, id ) t1 cross join
(select #w1 := -1, #rn1 := -1) params
) t1 join
(select t2.*,
(#rn2 := if(#w2 = ward_id, #rn2 + 1,
if#w2 := ward_id, 1, 1)
)
) as rn
from (select t2.* from table2 t2 order by ward_id, id ) t2 cross join
(select #w2 := -1, #rn1 := -1) params
) t1
on t2.ward_id = t1.ward_id and t2.rn = t1.rn;
The subqueries enumerate the rows in each table. The join then uses the enumeration.
This is much simpler in MySQL 8.0, using row_number().
I'm assuming here that ID is intended to be the same from both tables. If so, I think you can do a multi-condition join:
select * from table1 t1
inner join table2 t2
on t1.ID=t2.ID and t1.WARD_ID=t2.WARD_ID
You can do something like:
SET #rn:=0;
SET #rn2:=0;
SELECT *
FROM (
SELECT #rn:=#rn+1 AS rn1, t1.ID, t1.NAME, t1.WARD_ID
FROM t1
GROUP BY t1.WARD_ID, t1.NAME
ORDER BY t1.WARD_ID, t1.NAME
) s1
INNER JOIN (
SELECT #rn2:=#rn2+1 AS rn2, t2.ID, t2.MONTH1, t2.MONTH2, t2.WARD_ID
FROM t2
GROUP BY t2.WARD_ID, t2.MONTH1,t2.MONTH2
ORDER BY t2.WARD_ID, t2.MONTH1,t2.MONTH2
) s2 ON s1.WARD_ID = s2.WARD_ID
AND s1.rn1 = s2.rn2
But it really doesn't reliably sort the tables to join the same rows every time. I still think there isn't a reliable/repeatable way to join the two tables the same every time.
============================================================
http://sqlfiddle.com/#!9/aa2db0/1 <<<< If ID can be used to reliably sort the two tables, you can use it in the ORDER BYs. I've added it in this Fiddle, and included rows in the setup that would fall before the existing records and potentially change the sorting. This also includes more records in Table 2 than there are in Table 1, so would possibly result in duplicated rows. These new rows are ignored since they can't be matched between tables.

SQL DELETE all rows apart from last N rows for each unique value

Here's a tough one,
How would I delete all but the last, say 3 rows, for each unique value in a different field?
Here's a visual of the problem:
id | otherfield
---------------
1 | apple <- DELETE
2 | banana <- KEEP
3 | apple <- DELETE
4 | apple <- KEEP
5 | carrot <- KEEP
6 | apple <- KEEP
7 | apple <- KEEP
8 | banana <- KEEP
How would I accomplish this in SQL?
Non tested, but something along these lines might work:
DELETE t.*
FROM table t JOIN (
SELECT id
#rowNum := IF(#otherfield <> otherfield, 1, #rowNum + 1) rn,
#otherfield := otherfield otherfield
FROM (
SELECT id, otherfield
FROM table
ORDER BY otherfield, id DESC
) t, (SELECT #otherfield := NULL, #rowNum := -1) dm
) rs ON t.id = rs.id
WHERE rs.rn > 3
Delete MyTable
Where Id In (
Select Id
From (
Select Id
, (Select COUNT(*)
From MyTable As T2
Where T2.OtherField = T.OtherField
And T2.Id > T.Id) As Rnk
From MyTable As T
) As Z
Where Z.Rnk > 2
)
Another version which might be a bit faster:
Delete MyTable
Where Id In (
Select T.Id
From MyTable As T
Left Join MyTable As T2
On T2.OtherField = T.OtherField
And T2.Id > T.Id
Group By T.Id
Having Count(T2.Id) > 2
)