Table01 with columns
| Id1 | CommaSeparated |
| 1 | 1,2,3 |
| 2 | 4 |
| 3 | 7,8 |
Table02 with columns
| Id2 | SomeValue |
| 1 | Value01 |
| 7 | Value02 |
| 8 | Value03 |
This works: SELECT SomeValue FROM Table02 WHERE Id2 IN(7,8);. Get Value02 and Value03.
But SELECT SomeValue FROM Table02 WHERE Id2 IN( SELECT CommaSeparated FROM Table01 WHERE Id1 = ? ); does not work (get only Value02). Because it takes only the first character/integer from 7,8.
Tried this
SELECT SomeValue FROM Table02 WHERE FIND_IN_SET ( Id2, ( SELECT CommaSeparated FROM Table01 WHERE Id1 = 3 ) ) > 0;
But returns no results...
Any ideas how to solve? Or better to create another table where "connect" the both tables ("normalize data")?
You can do it using with and json_table to transfrom comma separated string to rows :
with t1 as (
select Id1, CommaSeparated
from table01
)
select t2.SomeValue
from t1
join json_table(
replace(json_array(t1.CommaSeparated), ',', '","'),
'$[*]' columns (CommaSeparated varchar(50) path '$')
) t
join table02 t2 on t2.Id2 = t.CommaSeparated
where t1.Id1 = 3
Demo here
Solution, using "linking" table.
LinkingTable
| Id | Id1 | Id2 |
| 1 | 3 | 7 |
| 2 | 3 | 8 |
Table01
| Id1 | CommaSeparated |
| 1 | 1,2,3 |
| 2 | 4 |
| 3 | 7,8 |
Table02
| Id2 | SomeValue |
| 1 | Value01 |
| 7 | Value02 |
| 8 | Value03 |
And sql like this SELECT SomeValue FROM Table02 WHERE Id2 IN( SELECT Id2 FROM LinkingTable WHERE Id1 = 3 );
Only need to change code, while inserting into Table02, need to insert also into LinkingTable and use/take Id1 from Table01
Or in my case, i anyway need to select data from Table01. So another solution SELECT CommaSeparated FROM Table01 WHERE Id1 = 3; For example, result name as $arr_result.
Then
SELECT SomeValue FROM Table02 WHERE Id2 IN( '.
rtrim( str_repeat( '?,', count(explode( ',', trim($arr_result[0]['CommaSeparated ']) )) ), ',' ).
')';
If i use LinkingTable, i also get "waste of resources". Because in LinkingTable need to insert modified lastInsertId from Table02 and Id from Table01.
The third solution (a bit "crazy").
$sql_select = '
SELECT ( SELECT IFNULL (
( SELECT `T02`.`SomeValue`
FROM Table01 `T01`
INNER JOIN Table02 `T02`
ON FIND_IN_SET( `T02`.`Id2`, `T01`.`CommaSeparated` )
WHERE `T01`.`Id1 ` = 3 AND <another condition>
)
, "empty first value" ) )
UNION
SELECT ( SELECT IFNULL (
( SELECT `T02`.`SomeValue`
FROM Table01 `T01`
INNER JOIN Table02 `T02`
ON FIND_IN_SET( `T02`.`Id2`, `T01`.`CommaSeparated` )
WHERE `T01`.`Id1 ` = 3 AND <another condition>
)
, "empty second value" ) )
;';
And $arr_select = $stmt_select->fetchAll(PDO::FETCH_COLUMN, 0);
Get one dimensional array. May use if know maximum number of values (how many values, so many union).
At the moment conclusion is that LinkingTable solution would be better (less possibility to get something unexpected and, yes, seems in total uses less system resources) ...
Related
i have table with 2 columns like below
+----------+----------+
| Column A | Column B |
+----------+----------+
| 123 | ABC |
| 123 | XYC |
| 123 | FGH |
| 145 | QWE |
| 147 | YUI |
+----------+----------+
I want to select all values from table but view it like below:
+----------+---------+---------+----------+
| Column A | value 1 | value 2 | value 3 |
+----------+---------+---------+----------+
| 123 | ABC | XYC | FGH |
| 145 | QWE | | |
| 147 | YUI | | |
+----------+---------+---------+----------+
If you're not trying to create extra columns in your output, you can simply use GROUP_CONCAT with the separator of your choice. For example:
SELECT `Column A`,
GROUP_CONCAT(`Column B` SEPARATOR ' | ') AS `Values`
FROM table1
GROUP BY `Column A`
Output:
Column A Values
123 ABC | XYC | FGH
145 QWE
147 YUI
Demo on dbfiddle
I'm not sure how are you going to execute the query? but if you can manage to create dynamic SQL query string to find all duplicates rows and insert each row into a temp table and other values (unique) into a separate temp table. Then create another query to join all temp tables (with duplicate) value into a new data set, union all of them with the (unique) data set.
It may be a long and not a good solution but here's my experiment:
Insert all duplicates rows into #temp tables (3 rows= 3 #temp tables)
SELECT Id,Name
INTO #temp1
FROM TestTable
WHERE Name='ABC'
SELECT Id,Name
INTO #temp2
FROM TestTable
WHERE Name='XYC'
SELECT Id,Name
INTO #temp3
FROM TestTable
WHERE Name='FGH'
Insert all unique rows into single #temptable
SELECT Id,Name
INTO #temp4
FROM TestTable
WHERE Id!=123
Query
SELECT t1.Id,t1.Name as Value1,t2.Name as Value2,t3.Name as Value3
FROM #temp1 t1
INNER JOIN #temp2 t2 on t1.Id=t2.Id
INNER JOIN #temp3 t3 on t1.Id=t3.Id
UNION ALL
SELECT t4.Id,t4.Name as Value1,null as Value2,null as Value3
FROM #temp4 t4
Result
If you want three different columns, you can use row_number() and conditional aggregation:
select a,
max(case when seqnum = 1 then b end) as b_1,
max(case when seqnum = 2 then b end) as b_2,
max(case when seqnum = 3 then b end) as b_3
from (select t.*,
row_number() over (partition by a order by b) as seqnum
from t
) t
group by a;
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');
I want to show my parent id with child record(duplicate record). Here is my table
ID|Name |Comments|
__|_____|________|_
1 |Test1|Unique |
2 |Test2|Unique |
3 |Test1|Unique |
4 |Test2|Unique |
5 |Test1|Unique |
6 |Test3|Unique |
Expected Result:
ID|Name |Comments |
__|_____|__________________|_
1 |Test1|Unique |
2 |Test2|Unique |
3 |Test1|Duplicate with: 1 |
4 |Test2|Duplicate with: 2 |
5 |Test1|Duplicate with: 1 |
6 |Test3|Unique |
not sure what the exact goal here, but here is a single query that get the job done:
mysql> select ID,tbl.Name,if(no!=ID,concat('Duplicate with: ',no),'Unique') Comments from tbl left join (select ID no,Name from tbl group by Name) T on T.Name=tbl.Name;
+----+-------+-------------------+
| ID | Name | Comments |
+----+-------+-------------------+
| 1 | Test1 | Unique |
| 2 | Test2 | Unique |
| 3 | Test1 | Duplicate with: 1 |
| 4 | Test2 | Duplicate with: 2 |
| 5 | Test1 | Duplicate with: 1 |
| 6 | Test3 | Unique |
+----+-------+-------------------+
Check This Live Demo using 'coalesce' and 'Case when'
Query :
select id
,name
,coalesce(
( select coalesce(case when min(id)>0 then concat('Duplicate with : ',min(id)) else null end,Comments)
from Yourtable t2 where t2.name = t.name and t2.id < t.id group by Comments)
,Comments) as Comments
from Yourtable t
order by id
Output :
hey you can try this query and it is giving expected result.
select t1.id,t1.`name`,
CASE WHEN (select count(`name`) from table_name t2 where t2.`name` = t1.`name` and t2.id <= t1.id ) = 1
then 'unique'
else CONCAT('Duplicate with :',(select min(t3.id) from table_name t3 where t3.name = t1.`name`))
end as 'comments'
from table_name t1
replace table_name with your table.
Hope this works for you. Ask if any doubt
Using only a single sub-query.
select id
,name
,coalesce
(
concat
(
'Duplicate with: '
,(select min(id) from mytable t2 where t2.name = t.name and t2.id < t.id)
)
,'Unique'
) as Comments
from mytable t
order by id
I want two display the result of the second table 'e_value', wich are two records (from only one column), as two columns for the select query from first table 'e_order_item'.
Also I am displaying many order items using a parameter 'collect_id',
so I want to display each two values of the table 'e_value' using to the order item id displayed on the select query.
for example, I have this on the tables
+-------------------------------+
| e_order_item |
+-------------------------------+
| oi_id oi_price oi_collect_id |
| 1 100 2 |
| 2 30 2 |
| 3 55 3 |
| 4 70 4 |
| 5 220 2 |
| 6 300 2 |
+-------------------------------+
+----------------------------+
| e_value |
+----------------------------+
| v_id v_value v_oi_id |
| 1 name1 1 |
| 2 surname1 1 |
| 3 name2 2 |
| 4 surname2 2 |
| 5 name3 5 |
| 6 surname3 5 |
+----------------------------+
I want to select the order_items that have collect_id = 2, and I want the result to be like this
+--------------------------------------------------+
| |
+--------------------------------------------------+
| Result |
| oi_id oi_price oi_collect_id name surname |
| 1 100 2 name1 surname1 |
| 2 30 2 name2 surname2 |
| 5 220 2 name3 surname3 |
| 6 300 2 null null |
| |
+--------------------------------------------------+
Here's the query:
SELECT
t.oi_id,
t.oi_price,
t.oi_collect_id,
LEFT (
GROUP_CONCAT(t.v_value),
IF (
LOCATE(',',GROUP_CONCAT(t.v_value)) = 0,
LENGTH(GROUP_CONCAT(t.v_value)),
LOCATE(',', GROUP_CONCAT(t.v_value)) - 1
)
) 'Name',
RIGHT (
GROUP_CONCAT(t.v_value),
LENGTH(GROUP_CONCAT(t.v_value)) -
IF (
LOCATE(',',GROUP_CONCAT(t.v_value)) = 0,
LENGTH(GROUP_CONCAT(t.v_value)),
LOCATE(',',GROUP_CONCAT(t.v_value))
)
) Surname
FROM
(
SELECT
*
FROM e_order_item
LEFT JOIN e_value ON e_order_item.oi_id = e_value.v_oi_id
WHERE e_order_item.oi_collect_id = 2
ORDER BY oi_id, v_id
) t
GROUP BY t.oi_id;
DEMO HERE
Note:
The following example illustrates how we can get the first string and second string from a comma separated string.
SET #str := 'A,BCDEFGHIJKL';
SELECT
LEFT(#str,IF(LOCATE(',',#str) = 0, LENGTH(#str),LOCATE(',',#str)-1)) AS StringBeforeComma,
RIGHT(#str,LENGTH(#str)-IF(LOCATE(',',#str)=0,LENGTH(#str),LOCATE(',',#str))) AS StringAfterComma
Result:
StringBeforeComma StringAfterComma
A BCDEFGHIJKL
You have to go for pivoting to get the desired result.
select oi_id, oi_price, oi_collect_id
, max(name) as name
, max(surname) as surname
from (
select
i.oi_id, i.oi_price, i.oi_collect_id
, case when #prevVal <> (#currVal:=v.v_oi_id)
then v.v_value
else null
end as name
, case when #prevVal = #currVal
then v.v_value
else null
end as surname
, #prevVal:=#currVal as temp_currVal
from e_order_item i
left join e_value v on v.v_oi_id = i.oi_id,
(select #prevVal:=-1, #currVal:=-1) as inits
where i.oi_collect_id=2
) as main_data
group by oi_id, oi_price, oi_collect_id
order by 1;
This is tested and run successfully...and give output as you want...
There are two subqueries:
1.First will give all result having collect_id = 2...
1.SELECT tab1.oi_id, tab1.oi_price, tab1.oi_collect_id
from(
SELECT oi_id, oi_price, oi_collect_id
from e_order_item
where oi_collect_id = 2
) as tab1;
2.This query will give you name, surname and id in different columns..
2.(SELECT e.v_value as name, surname, id
from (
select t1.v_value as surname, t1.v_oi_id as id from e_value as t1
group by t1.v_oi_id
)join e_value as e on id = e.v_oi_id and surname <> e.v_value
) as tab2 on tab1.oi_id = tab2.id;
Now left join these two query to get our desired result as:
SELECT tab1.oi_id, tab1.oi_price, tab1.oi_collect_id, name, surname
from(
SELECT oi_id, oi_price, oi_collect_id
from e_order_item
where oi_collect_id = 2
) as tab1 left join
(SELECT e.v_value as name, surname, id
from (
select t1.v_value as surname, t1.v_oi_id as id from e_value as t1
group by t1.v_oi_id
)join e_value as e on id = e.v_oi_id and surname <> e.v_value
) as tab2 on tab1.oi_id = tab2.id
order by tab1.oi_id asc; // to print in ascending order..
Why we use left join..You can use this link http://www.w3schools.com/sql/sql_join_left.asp to understand properly...
If this solution is helpful then let me know...
I have a table1 (records 3), and table2 (records 3).
Where i have field name in both.
Now i want to make a result from those two table
which will show me both table records and take only one if there is duplicate.
from that result i will do main query using like or or other logical statements
So my expected output records will contain 5 rows not 6 rows. How do i do that?
Example:
table1: table2:
+-------------------------+ +--------------------------------+
| Name | ID | Name | ID
+-------------------------- +---------------------------------
| A | 1 | 1 December Name | 4
| B | 2 | D | 5
| 1 December Name | 3 | E | 6
My Expected output is following which works, but does not work when i use WHERE
like to only get '1 December Name':
+-----------------------------------------------------+
| Name | ID
+-----------------------------------------------------
| A | 1 table1
| B | 2 table1
| 1 December Name | 3 table2 or table1 (no unique)
| D | 4 table2
| E | 5 table2
I tried this:
SELECT * FROM
(
(
SELECT name AS name FROM table1
)
UNION
(
SELECT anothername AS name FROM table2
)
) as t
WHERE name like '%1 December Name%'
limit 1,10
Output: Your SQL query has been executed successfully ( Query took 0.2798 sec )
Problem: The following query has no error but it does not find that record which contain '1 December Name'
Follow up: works i know now which ID it used
SELECT NAME, ID, STATUS FROM
(
(
SELECT NAME AS name , id, CONCAT('table1') AS STATUS FROM table1
)
UNION ALL
(
SELECT ANOTHERNAME AS name, id, CONCAT( 'table2' ) AS STATUS FROM table2
)
) AS t
WHERE
t.NAME LIKE '%1 December Name%'
LIMIT 1 , 10;
You can get something similar to what you want:
select name, group_concat(id) from
(select name, 'table1' as id from table1
union all
select name, 'table2' from table2) x
group by name
Output would be:
+------------------------------------+
| Name | ID
--------------------------------------
| A | table1
| B | table1
| 1 December Name | table1,table2
| D | table2
| E | table2
UNION ALL is the right choice (not UNION), because it does not remove duplicates, and preserves row order
Try this
(SELECT name FROM table1 )
UNION (SELECT name FROM table2);