SQL - Joining two Select results horizontal - mysql

I am creating the following two temporary tables t1 and t2 using two SELECT statements:
+------+------+
| Col1 | Col2 |
+------+------+
| A | 1 |
| B | 2 |
| C | 3 |
+------+------+
and
+------+------+
| Col3 | Col4 |
+------+------+
| C | 5 |
| D | 6 |
| E | 7 |
+------+------+
The two SELECT statements by nature are always returning the same number of rows. Now I want to join/combine these two results horizontally to get the following output table:
+------+------+------+------+
| Col1 | Col2 | Col3 | Col4 |
+------+------+------+------+
| A | 1 | C | 5 |
| B | 2 | D | 6 |
| C | 3 | E | 7 |
+------+------+------+------+
I tried working with multiple JOIN statement, but could figure out a smart way. I also tried the UNION statement, which delivered a vertical join, but not the required horizontal version.
Here two easy SELECT statement for better orientation in possible solutions:
SELECT * FROM `t1` WHERE date = DATE(NOW())
SELECT * FROM `t2` WHERE date = DATE(NOW())
Thanks in advance for your help.

Try this
SET #row_number_t1:=0;
SET #row_number_t2:=0;
SELECT t1_modif.*, t2_modif.* FROM
(SELECT #row_number_t1:=#row_number_t1+1 AS row_number,
t1.* FROM t1)
t1_modif
JOIN (SELECT #row_number_t2:=#row_number_t2+1 AS row_number,
t2.* FROM t2)
t2_modif ON t2_modif.row_number = t1_modif.row_number
Note that order is not guaranteed, to do this add ORDER BY clause at the end of each FROM t1 and FROM t2 subqueries, basically we are joining by row_number, since MySQL doesn't have ROW_ID, ROW_NUM (similar to mssql, oracle, postgres) we have used session variables

Creating Virtual IDs for relation. This is not a recommended way though.
SELECT col1, col2, col3, col4 FROM
(SELECT t1.*, (#t1VID := #t1VID + 1) t1VID FROM t1 , (SELECT #t1VID := 0) d) e
JOIN (SELECT t2.*, (#t2VID := #t2VID + 1) t2VID FROM t2, (SELECT #t2VID := 0) a ) b ON t1VID = t2VID
JOIN (SELECT #t1VID := 0) c ;
Ideal solution would have been adding proper relation between the tables. If not, it is best to query it separately and do the necessary joining in application layer

Related

in sum, use case to compare incoming value with another column (MYSQL)

ie:
| id | num |
| a | 1 |
| b | 2 |
| c | 3 |
| d | 4 |
| e | 5 |
and this query is essentially what I'm trying to do:
select num as number, sum(case num > number then num else 0 end) as summation from table;
(I'm trying to sum up all the ints larger than the currently selected num in the column num.)
example output from above table:
| num | summation |
| 1 | 14 |
| 2 | 12 |
| 3 | 9 |
| 4 | 5 |
|5 | 0 |
The problem lies in the fact that I can't use the alias defined in the same select statement; is there another way?
Thanks!
If you're on MySQL 8.0 you can use window functions.
SELECT num,
sum(num) OVER (ORDER BY num DESC) - num summation
FROM elbat
ORDER BY num;
Prior to MySQL 8.0 you can use a correlated subquery.
SELECT t1.num,
coalesce((SELECT sum(t2.num)
FROM elbat t2
WHERE t2.num > t1.num),
0) summation
FROM elbat t1
ORDER BY t1.num;
You can write this using a correlated subquery:
select num,
(select sum(num)
from t 2
where t2.num >= t.num
) - num as summation
from t;
You can use correlated subquery :
select num,
(select sum(num)
from table t2
where t2.num > t.num
) as summation
from table t1;
this works, although a bit messy:
select num, (select sum(case when table.num > temp.num then num else 0 end)
from (select * from table) as temp
) as summation
from table;

How to select all rows from group, until occurrence of a value

I'm trying to extract all rows from same Group until I hit breakpoint value B. The example data below is ordered virtual table:
+----+--------+------------+
| ID | Group | Breakpoint |
+----+--------+------------+
| 1 | 1 | A |
| 2 | 1 | A |
| 3 | 1 | B |
| 4 | 1 | A |
| 5 | 2 | A |
| 6 | 2 | A |
| 7 | 2 | A |
| 8 | 3 | A |
| 9 | 3 | B |
+----+--------+------------+
This would be my result.
+----+--------+------------+
| ID | Group | Breakpoint |
+----+--------+------------+
| 1 | 1 | A |
| 2 | 1 | A |
| 5 | 2 | A |
| 6 | 2 | A |
| 7 | 2 | A |
| 8 | 3 | A |
+----+--------+------------+
Notice that when there are both A and B breakpoint values within a group, I want to have the rows until the first A value in this order. If there are only A values for a group like in group 2, I want to have all of the items in the group.
Here's a simple solution that uses no subqueries or GROUP BY logic.
SELECT t1.ID, t1.Group, t1.Breakpoint
FROM MyTable AS t1
LEFT OUTER JOIN MyTable AS t2
ON t1.ID >= t2.ID AND t1.`Group` = t2.`Group` AND t2.Breakpoint = 'B'
WHERE t2.ID IS NULL
For each row t1, try to find another row t2 with 'B', in the same Group, with an earlier ID. If none is found, the OUTER JOIN guarantees that t2.ID is NULL. That will be true only up until the desired breakpoint.
From you example above, you are not really grouping the results. you just need to display the records where Breakpoint is A:
Select * From Table
Where Breakpint ='A'
You may use NOT EXISTS
select *
from your_table t1
where not exists (
select 1
from your_table t2
where t1.group = t2.group and t2.id <= t1.id and t2.breakpoint = 'B'
)
or ALL can work as well if you never have NULL in id
select *
from your_table t1
where t1.id < ALL(
select t2.id
from your_table t2
where t1.group = t2.group and t2.breakpoint = 'B'
)
Assuming that we are ordering by ID column, we could do something like this:
SELECT d.*
FROM mytable d
LEFT
JOIN ( SELECT bp.group
, MIN(bp.id) AS bp_id
FROM mytable bp
WHERE bp.breakpoint = 'B'
GROUP BY bp.group
) b
ON b.group = d.group
WHERE b.bp_id > d.id OR b.bp_id IS NULL
ORDER BY d.group, d.id
This takes into account cases where there is no breakpoint='B' row for a given group, and returns all of the rows for that group.
Note that the inline view b gets us the lowest id value from rows with breakpoint='B' for each group. We can outer join that to original table (matching on group), and then conditional tests in the WHERE clause to exclude rows that follow the first breakpoint='B' for each group.
SQL tables represent unordered sets. Hence, there is no "before" or "after" a particular row.
Let me assume that you have some column that specifies the ordering. I'll call it id. You can then do what you want with:
select t.*
from t
where t.id < (select min(t2.id) from t t2 where t2.group = t.group and t2.breakpoint = 'B');
To get all rows when if there are no 'B':
select t.*
from t
where t.id < (select coalesce(min(t2.id), t.id + 1) from t t2 where t2.group = t.group and t2.breakpoint = 'B');

Mysql - Select at least one or select none

I have a table as so...
----------------------------------------
| id | name | group | number |
----------------------------------------
| 1 | joey | 1 | 2 |
| 2 | keidy | 1 | 3 |
| 3 | james | 2 | 2 |
| 4 | steven | 2 | 5 |
| 5 | jason | 3 | 2 |
| 6 | shane | 3 | 3 |
----------------------------------------
I'm running a select like so:
SELECT * FROM table WHERE number IN (2,3);
The problem im trying to solve is that I want to only grab get results from groups that have 1 or more rows of each number. For instance the above query is returning id's 1-2-3-5-6, when I'd like the results to exclude id 3 since the group of '2' can only return 1 result for the number of '2' and not for BOTH 2 and 3, since there's no row with the number 3 for the group 2 i'd like it to not even select id 3 at all.
Any help would be great.
Try it this way
SELECT *
FROM table1 t
WHERE number IN(2, 3)
AND EXISTS
(
SELECT *
FROM table1
WHERE number IN(2, 3)
AND `group` = t.`group`
GROUP BY `group`
HAVING MAX(number = 2) > 0
AND MAX(number = 3) > 0
)
or
SELECT *
FROM table1 t JOIN
(
SELECT `group`
FROM table1
WHERE number IN(2, 3)
GROUP BY `group`
HAVING MAX(number = 2) > 0
AND MAX(number = 3) > 0
) q
ON t.`group` = q.`group`;
or
SELECT *
FROM table1
WHERE `group` IN
(
SELECT `group`
FROM table1
WHERE number IN(2, 3)
GROUP BY `group`
HAVING MAX(number = 2) > 0
AND MAX(number = 3) > 0
);
Sample output (for both queries):
| ID | NAME | GROUP | NUMBER |
|----|-------|-------|--------|
| 1 | joey | 1 | 2 |
| 2 | keidy | 1 | 3 |
| 5 | jason | 3 | 2 |
| 6 | shane | 3 | 3 |
Here is SQLFiddle demo
On this, you can approach from a fun way with multiple joins for what you WANT qualified, OR, apply a prequery to get all qualified groups as others have suggested, but readability is a bit off for me..
Anyhow, here's an approach going through the table once, but with joins
select DISTINCT
T.id,
T.Name,
T.Group,
T.Number
from
YourTable T
Join YourTable T2
on T.Group = T2.Group AND T2.Group = 2
Join YourTable T3
on T.Group = T3.Group AND T3.Group = 3
where
T.Number IN ( 2, 3 )
So on the first record, it is pointing to by it's own group to the T2 group AND the T2 group is specifically a 2... Then again, but testing the group for the T3 instance and T3's group is a 3.
If it cant complete the join to either of the T2 or T3 instances, the record is done for consideration, and since indexes work great for joins like this, make sure you have one index for your NUMBER criteria, and another index on the (GROUP, NUMBER) for those comparisons and the next query sample...
If doing by more than this simple 2, but larger group, prequery qualified groups, then join to that
select
YT2.*
from
( select YT1.group
from YourTable YT1
where YT1.Number in (2, 3)
group by YT1.group
having count( DISTINCT YT1.group ) = 2 ) PreQualified
JOIN YourTable YT2
on PreQualified.group = YT2.group
AND YT2.Number in (2,3)
Maybe this,if I understand you
SELECT id FROM table WHERE `group` IN
(SELECT `group` FROM table WHERE number IN (2,3)
GROUP BY `group`
HAVING COUNT(DISTINCT number)=2)
SQL Fiddle
This will return all ids where BOTH numbers exist in a group.Remove DISTINCT if you want ids for groups where just one numbers is in.

Select Rows with maximum column value grouped by another column without nested select statement

I know that this is a duplicate of Select Rows with Maximum Column Value group by Another Column but I want to select rows that have the maximum column value,as group by another column , but without nested select statement, I know it can be done like this:
SELECT
T.Name,
T.Rank,
T.ID
FROM MyTable T
WHERE T.Rank = (
SELECT MAX( T1.Rank) FROM MyTable T1
WHERE T1.Name= T.Name
)
where ID,
Rank,
Name is the table schema, and I want to group by results by Name first, and then choose one row from each Name group, depending on which one has the highest Rank.
Attached is a sample of the table I want to select from
mysql> SELECT t1.nm, t1.rank,t1.id
FROM mytable t1
LEFT JOIN (
SELECT nm, max(rank) as top
FROM mytable t2
GROUP BY nm
) AS t2 ON t1.nm=t2.nm AND t1.rank = t2.top
WHERE t2.nm IS not NULL
ORDER BY nm;
+----+------+---------+
| nm | rank | id |
+----+------+---------+
| m | -1 | b7kjhsf |
| n | 13 | d3sf |
+----+------+---------+
2 rows in set (0.00 sec)
mysql> select * from mytable;
+----+------+----------+
| nm | rank | id |
+----+------+----------+
| n | 11 | asfd |
| n | 11 | bsf |
| n | 11 | zzasdfsf |
| n | 13 | d3sf |
| n | 11 | effesf |
| n | 10 | yxxgesf |
| n | 11 | bkhjusf |
| m | -1 | b7kjhsf |
| m | -4 | cdfgabsf |
+----+------+----------+
9 rows in set (0.00 sec)
As mentioned in the other answer, the only other alternative that I know of, is using Common Table Expressions:
;WITH CTE AS
(
T.Name,
T.Rank,
T.ID,
ROW_NUMBER() OVER
(PARTITION BY Name ORDER BY Rank DESC)
AS RowNumber
FROM MyTable
)
SELECT *
FROM CTE
WHERE RowNumber = 1
SELECT Name, Id, Rank FROM
(
SELECT T.Name, T.Id, T.Rank, RANK() OVER (PARTITION BY T.Name ORDER BY T.Rank DESC) = 1 AS NameRank
FROM MyTable T
)
WHERE NameRank = 1
Not sure whether you are just trying to exclude the nested select, and whether joining aginst a subselect would be acceptable. If so:-
SELECT
T.Name,
T.Rank,
T.ID
FROM MyTable T
INNER JOIN (SELECT Name, MAX(Rank) AS MaxRank FROM MyTable GROUP BY Name ) T1
ON T.Name = T1.Name
AND T.Rank = T1.MaxRank

MySQL SELECT function to sum current data

I have a query that return something like this:
| ID | Val |
| 0 | 10 |
| 1 | 20 |
| 2 | 30 |
But instead of that, I want something like this:
| ID | Val | Sum |
| 0 | 10 | 10 |
| 1 | 20 | 30 |
| 2 | 30 | 60 |
Is that a way to do it on the query (I'm using MySQL)?
Tks
This is called cumulative sum.
In Oracle and PostgreSQL, it is calculated using a window function:
SELECT id, val, SUM() OVER (ORDER BY id ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
FROM mytable
However, MySQL does not support it.
In MySQL, you can calculate it using session variables:
SET #s = 0;
SELECT id, val, #s := #s + val
FROM mytable
ORDER BY
id
;
or in a pure set-based but less efficient way:
SELECT t1.id, t1.val, SUM(t2.val)
FROM mytable t1
JOIN mytable t2
ON t2.id <= t1.id
GROUP BY
t1.id
;
Would something like this work for your purposes? (Warning, potentially really darned slow with the subselect).
SELECT t1.id, t1.val, (SELECT SUM(val) FROM table AS t2 WHERE t2.id <= t1.id) 'sum'
FROM table AS t1
ORDER BY id ASC
Assuming the table name is t, you can use a query like this:
select t.id, t.val, sum(t2.val) Sum
from t, t t2
where t2.id <= t.id
group by t.id, t.val
(tested in Oracle)