distinct values of different columns - mysql

I'm not sure it's possible.
I have a table with 3 columns, and I want to get different values of each one, there is an example:
A B C
--------------------
a1 b1 c1
a1 b2 c2
a1 b3 c1
The expected result is:
A B C
-----------------
a1 b1 c1
b2 c2
b3
So, it will be the union of:
SELECT DISTINCT A FROM myTable
SELECT DISTINCT B FROM myTable
SELECT DISTINCT C FROM myTable
Using UNION I get the result in one column.
Is this posible to do?

Please try the following...
SELECT COALESCE( a, '' ) AS a,
COALESCE( b, '' ) AS b,
COALESCE( c, '' ) AS c
FROM ( SELECT a,
ROW_NUMBER() OVER ( ORDER BY a ) AS recordNumber
FROM ( SELECT a
FROM myTable
GROUP BY a
) aFinder
) aNumberedFinder
FULL JOIN ( SELECT b,
ROW_NUMBER() OVER ( ORDER BY b ) AS recordNumber
FROM ( SELECT b
FROM myTable
GROUP BY b
) bFinder
) bNumberedFinder ON bNumberedFinder.recordNumber = aNumberedFinder.recordNumber
FULL JOIN ( SELECT c,
ROW_NUMBER() OVER ( ORDER BY c ) AS recordNumber
FROM ( SELECT c
FROM myTable
GROUP BY c
) cFinder
) cNumberedFinder ON cNumberedFinder.recordNumber = aNumberedFinder.recordNumber
OR cNumberedFinder.recordNumber = bNumberedFinder.recordNumber;
This statement starts with the following subquery...
SELECT a
FROM myTable
GROUP BY a
This subquery produces a list of the unique values in field a. The following subquery reproduces this list with record (aka row) numbers...
SELECT a,
ROW_NUMBER() OVER ( ORDER BY a ) AS recordNumber
FROM ( SELECT a
FROM myTable
GROUP BY a
) aFinder
Similar subqueries are used to produce numbered lists of the unique values in fields b and c.
A FULL OUTER JOIN (abbreviated here to FULL JOIN) is then performed between the lists for a and b, with their record numbers being used as the common / joining value.
Note : An INNER JOIN would only have returned as many records as there are in the shorter list of the two. A LEFT JOIN would only have been effective if the list on the left of the JOIN were of greater or longer length than the list on the right. Similar logic applies to the usage of a RIGHT JOIN. A FULL JOIN will join the two lists in the manner of a LEFT JOIN or RIGHT JOIN irrespective of which list is longer.
A FULL JOIN is then performed between the above joined dataset and the list for c where it finds a common value in either list.
The values of a, b and c are then selected from the finally joined dataset, with the COALESCE() function replacing the occurrence of any NULL values, such as those generated by the joining process, with the empty string ('')
If you have any questions or comments, then please feel free to post a Comment accordingly.
Appendix
My statement was tested against a database created using the following script...
CREATE TABLE myTable
(
a VARCHAR( 5 ),
b VARCHAR( 5 ),
c VARCHAR( 5 )
);
INSERT INTO myTable ( a,
b,
c
)
VALUES ( 'a1', 'b1', 'c1' ),
( 'a1', 'b2', 'c2' ),
( 'a1', 'b3', 'c1' );
Further Reading
https://learn.microsoft.com/en-us/sql/t-sql/language-elements/coalesce-transact-sql (on using the COALESCE() function in Transact-SQL)
https://www.w3schools.com/sql/sql_join.asp (on the various types of horizontal JOIN - the Venn diagram is useful)
https://technet.microsoft.com/en-us/library/ms187518(v=sql.105).aspx (on using OUTER JOIN in SQL-Server)
https://learn.microsoft.com/en-us/sql/t-sql/functions/row-number-transact-sql (on using ROW_NUMBER() in Transact-SQL)

Related

How to filter out duplicates from a union query?

I have two similar SELECT queries that retrieve data from the same table "my_table".
-- 1st select
SELECT
my_table.id,
a,
b
FROM my_table
JOIN table2 ON u = v
JOIN table3 ON x = y
UNION ALL
-- 2st select
SELECT
my_table.id,
a,
b
FROM my_table
JOIN table2 ON r = s
JOIN table3 ON t = u
Duplicates are to be filtered out under the following conditions:
If the second select returns an id that is already present in the 1st select, it should be discarded.
Is there an easy solution without using a common table expression?
Note: The SQL does not have to be a UNION and can also be changed.
UNION filters out duplicate rows by default. UNION ALL does not remove duplicates.
But the duplicates are based on all columns being identical, not just the id column. If a given id value occurs in both queries, but any of the other two columns are different, then it counts as a distinct row.
If you want to reduce the result to a single row per id, the use a GROUP BY:
SELECT id, ...aggregate expressions...
FROM (
SELECT my_table.id, a, b ...
UNION
SELECT my_table.id, a, b ...
) AS t
GROUP BY id;
When you GROUP BY id, then any other expressions of the outer select-list must be in aggregate functions like MAX() or SUM(), etc.
The reason it is important to use an aggregate function is that when there are multiple rows with the same id value which you want to reduce to one row, what value should be displayed for a and b?
Example:
id
a
b
4
12
24
4
18
28
If you group by id, you would get one row for id=4, but what value for the other two columns?
id
a
b
4
?
?
Read https://dev.mysql.com/doc/refman/8.0/en/group-by-handling.html for more details on this. Or my answer to Reason for Column is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause
You must use an aggregate function, which includes GROUP_CONCAT() to append all the values from that column in a comma-separated list. Or you can use ANY_VALUE() which picks one of the values from that column arbitrarily.
I think this should do it:
-- 1st select
SELECT
my_table.id,
a,
b
FROM my_table
JOIN table2 ON u = v
JOIN table3 ON x = y
WHERE id NOT IN (
SELECT
my_table.id,
FROM my_table
JOIN table2 ON r = s
JOIN table3 ON t = u
)
UNION ALL
-- 2st select
SELECT
my_table.id,
a,
b
FROM my_table
JOIN table2 ON r = s
JOIN table3 ON t = u

How to retrieve all members from a transitive relationship through one SQL query [duplicate]

I have a table with following structure
Table name: matches
That basically stores which product is matching which product. I need to process this table
And store in a groups table like below.
Table Name: groups
group_ID stores the MIN Product_ID of the Product_IDS that form a group. To give an example let's say
If A is matching B and B is Matching C then three rows should go to group table in format (A, A), (A, B), (A, C)
I have tried looking into co-related subqueries and CTE, but not getting this to implement.
I need to do this all in SQL.
Thanks for the help .
Try this:
;WITH CTE
AS
(
SELECT DISTINCT
M1.Product_ID Group_ID,
M1.Product_ID
FROM matches M1
LEFT JOIN matches M2
ON M1.Product_Id = M2.matching_Product_Id
WHERE M2.matching_Product_Id IS NULL
UNION ALL
SELECT
C.Group_ID,
M.matching_Product_Id
FROM CTE C
JOIN matches M
ON C.Product_ID = M.Product_ID
)
SELECT * FROM CTE ORDER BY Group_ID
You can use OPTION(MAXRECURSION n) to control recursion depth.
SQL FIDDLE DEMO
Something like this (not tested)
with match_groups as (
select product_id,
matching_product_id,
product_id as group_id
from matches
where product_id not in (select matching_product_id from matches)
union all
select m.product_id, m.matching_product_id, p.group_id
from matches m
join match_groups p on m.product_id = p.matching_product_id
)
select group_id, product_id
from match_groups
order by group_id;
Sample of the Recursive Level:
DECLARE #VALUE_CODE AS VARCHAR(5);
--SET #VALUE_CODE = 'A' -- Specify a level
WITH ViewValue AS
(
SELECT ValueCode
, ValueDesc
, PrecedingValueCode
FROM ValuesTable
WHERE PrecedingValueCode IS NULL
UNION ALL
SELECT A.ValueCode
, A.ValueDesc
, A.PrecedingValueCode
FROM ValuesTable A
INNER JOIN ViewValue V ON
V.ValueCode = A.PrecedingValueCode
)
SELECT ValueCode, ValueDesc, PrecedingValueCode
FROM ViewValue
--WHERE PrecedingValueCode = #VALUE_CODE -- Specific level
--WHERE PrecedingValueCode IS NULL -- Root

Check if table a primary key is exist in table b

Table A:
ID, Name, etc.
Table B:
ID, TableA-ID.
SELECT * FROM A;
and I want to return a boolean value in the same result for this condition ( if A.ID Exists in Table B).
There are several ways of achieving what you need. Below are three possibilities. These all differ in execution plans and how database actually wants to execute them so depending on your record count one may be more efficient than the other. It's better if you see it for yourself.
1) Use LEFT JOIN and check if a non-null field from B is not null to ensure the record exists. Then apply DISTINCT clause if relationship is 1:N to only show rows from A without duplicates.
select distinct a.*, b.id is not null as exists_b
from a
left join b on
a.id = b.tablea-id
2) Use exists() function, which will be evaluated for each row being returned from table A.
select a.*, exists(select 1 from b where a.id = b.tablea-id) as exists_b
from a
3) Use a combination of subquery expression EXISTS and it's contradiction in two queries to check if a record has or has not a match within table B. Then UNION ALL to combine both results into one.
select *, true as exists_b
from a
where exists (
select 1
from b
where a.id = b.tablea-id
)
union all
select *, false as exists_b
from a
where not exists (
select 1
from b
where a.id = b.tablea-id
)
select A.*, IFNULL((select 1 from B where B.TableA-ID = A.ID limit 1),0) as `exists` from A;
The above statement will result in a 1, if the key exists, and a 0 if that key does not exist. Limit 1 is important if there are multiple records in B

pulling data from two different tables with common row elements

I have the following two tables:
Table a:
name qty
a 10
b 20
c 30
d 40
and table b
name qty
a 10
b 20
d 20
e 60.
I want to merge there two tables and create a new table like this
name qty
a 20
b 40
c 30
d 60
e 60
The objective is to add the values if there is have the same value in name or else just append the values in table two to table 1.
Unfortunately, MySQL does not support full outer join. Here is a method using union all and group by:
select name, sum(qty) as qty
from ((select name, qty from a) union all
(select name, qty from b)
) ab
group by name;
To simulate a full outer join, just execute a left outer join (gives all the rows of Table A with all matching rows of Table B or NULL) and a right outer join where Table A is NULL (gives all the rows of Table B that have no match in Table A -- matches are already provided in first query).
In the first query, there will always be a Qty value from Table A with either a Qty value or NULL from Table B. In the second query, there will only be a Qty value from Table B.
See Fiddle results.
select a.Name, a.Qty + IsNull( b.Qty, 0 ) as Qty
from #TableA a
left outer join #TableB b
on b.Name = a.Name
union all
select b.Name, b.Qty
from #TableA a
right outer join #TableB b
on b.Name = a.Name
where a.Name is null;
You may use union or union all with the same results. Since there is less processing required with union all, that's what I chose.

MySQL How do you INSERT INTO a table with a SELECT subquery returning multiple rows?

MySQL How do you INSERT INTO a table with a SELECT subquery returning multiple rows?
INSERT INTO Results
(
People,
names,
)
VALUES
(
(
SELECT d.id
FROM Names f
JOIN People d ON d.id = f.id
),
(
"Henry"
),
);
I WANT to populate the new table with all results returning from this subquery. How do I do this without getting a ERROR 1242 (21000): Subquery returns more than 1 row
INSERT INTO Results (People, names )
SELECT d.id, 'Henry'
FROM Names f
JOIN People d ON d.id = f.id
Combine the static string Henry with your SELECT query.
Here is what I've found that works well. It is a little long but many times extra data needs to be shuffled around.
Insert multiple rows into table1 from table2 with values.
EXAMPLES:
INSERT INTO table1 (col1, col2, col3, col4, col5)
SELECT col1,col2,col3,col4,col5
FROM table2 t2
WHERE t2.val2 IN (MULTIPLE VALUES)
AND (Another Conditional);
You can insert hard coded values to get insert multiple rows with repeat data:
INSERT INTO table1 (col1, col2, col3, col4, col5)
SELECT "Value", col2, col3, "1900-01-01","9999-12-31"
FROM table2 t2
WHERE t2.val2 IN (MULTIPLE VALUES)
AND (Another Conditional);
Note that: "Value","1900-01-01","9999-12-31" will repeat across all rows inserted.
INSERT INTO Results
(
People,
names,
)
SELECT d.id, 'Henry'
FROM Names f
JOIN People d ON d.id = f.id
INSERT INTO Results
(
People,
names,
)
VALUES
(
(
SELECT d.id
FROM Names f
JOIN People d ON (d.id = f.id) limit 1
),
(
"Henry"
),
);
The reason of this error (Subquery returns more than 1 row) is that you use parenthesis (). Look more careful to the best answer. It doesn't contain parethesis around subquery
In MySql multiple values from strings can be inserted like the following avoiding duplicates. Thanks.
insert into brand(name) select * from (
select 'Fender' as name
union select 'a'
union ..... ) t
where not exists (select 1 from brand t2 where t2.name COLLATE latin1_general_ci = t.name COLLATE utf8mb4_unicode_ci )
insert into ec_element(parentid,name) select elementid , 'STARTUP' from ec_element where name = 'BG';
insert statement takes values elementid from the table found with condition fulfilled and a label string.