Multiple SELECT statements in MySQL Query - mysql

If I have the schemas:
Type(a,b,c,d)
Name(e, b, g)
I am trying to find all the resulting Name's 'E' where the 'D' of the Type is greater than a number that we access using the shared 'B'.
I am trying to understand how to have multiple SELECT statements such as:
SELECT e FROM Name WHERE b = (SELECT b FROM Type WHERE d > 1);
Can someone explain the syntax error and how to do nested SELECT statements or do I have to join the two tables.
Thanks

This can be accomplished using a simple INNER JOIN operation:
SELECT DISTINCT n.e
FROM Name AS n
INNER JOIN Type AS t ON n.b = t.b
WHERE t.d > 1
You can also use EXISTS:
SELECT n.e
FROM Name AS n
WHERE EXISTS (SELECT 1
FROM Type AS t
WHERE n.b = t.b AND t.d > 1)

I think you want to use IN instead of =
SELECT e FROM Name WHERE b IN (SELECT b FROM Type WHERE d > 1);
= to compare one value to another value.
IN to see if a value exist whiten a list of multiple values.

Related

Check condition across several rows from a join

I have some entities A which can all have one or more B. How can I query "all entities A which have this and that from B"?
I can query "all entities A which have "this" or "that" from B":
SELECT *
FROM A
INNER JOIN B ON B.A_id = A.id
WHERE f = "this" OR f = "that"
GROUP BY A.ID
But I can’t check that they have "this" and "that", because I can’t check something across different rows from A.
I might use a GROUP_CONCAT, but then how can I effectively check that "blah,thing,foo,bar" has blah and foo without some unmaintainable REGEXP mess?
(I actually love regexes, but not so much in the context of an SQL query, for something that seems like it wouldn’t need a regex).
SELECT *, GROUP_CONCAT(f)
FROM A
INNER JOIN B ON B.A_ID = A.ID
WHERE f = "this" OR f = "that"
GROUP BY A.ID
How can I query "all entities A which have this and that from B"?
For such tasks we usually don't join, but use EXISTS or IN instead. E.g.
select *
from a
where id in (select a_id from b where f = 'this')
and id in (select a_id from b where f = 'that');
Here is a solution with an aggregation. In this particular case I see no advantage in using this. There are other situations, though, when an aggregation may be the appropriate solution.
select *
from a
where id in
(
select a_id
from b
group by a_id
having max(f = 'this') = 1
and max(f = 'that') = 1
);
In MySQL true is 1 and false is 0, so taking the maximum of a boolean expression tells us, whether there is at least one row for which the condition is true.
Your own query works, too, by the way, if you add the appropriate HAVING clause. And there is no regular expression matching needed for that. As your WHEREclause limits f to 'this' and 'that', your GROUP_CONCAT result can never be 'this,something_else;that', but only contain 'this' and 'that'. Well, depending on the table there may be duplicates, like 'this,this,that'. Use an ORDER BY clause and DISTINCT:
SELECT a.*
FROM a
INNER JOIN b ON b.a_id = a.id
WHERE b.f IN ('this', 'that')
GROUP BY a.id
HAVING GROUP_CONCAT(DISTINCT b.f ORDER BY b.f) = 'that,this';

How to return some predefined value when no row returned from big query with multiple column is selected

My query has multiple column being selected but now i want when no returned I want some dummy value to be returned.
For example in the following query:
SELECT a.abc, b.def, c.ghi
FROM comp a, damp b, champ c, omp d
WHERE a.id=b.id
and b.id=c.id
and c.id= d.id
ORDER BY a.abc desc
if there is no row returned I wanted to display atleast one column with some value, can somebody suggest me how can I achieve this.
I have already gone through some suggestion but none worked. Any help would be appreciated.
In oracle you could do something like this:
WITH mybigselect AS
(
SELECT a.abc, b.def, c.ghi
FROM comp a
JOIN damp b ON a.id = b.id
JOIN champ c ON b.id = c.id
JOIN omp d ON c.id = d.id
ORDER BY a.abc desc
)
SELECT * FROM mybigselect
UNION ALL
SELECT 'Nothing found', NULL, NULL FROM mybigselect
WHERE NOT EXISTS (SELECT * FROM mybigselect)
Note 1: that both rows in the UNION ALL needs to return columns of the same datatype. You can't return a number in the first column of SELECT * FROM mybigselect and "nothing found" in the query after UNION ALL
Note 2: rewrote the query using ANSI-JOIN style syntax.
I would recommend:
WITH cte as (
SELECT a.abc, b.def, c.ghi
FROM comp do JOIN
damp d
ON d.id = co.id JOIN
champ c
ON ch.id = d.id
omp o
ON o.id = ch.id
)
SELECT *
FROM cte
UNION ALL
SELECT 'Some value', NULL, NLL
FROM dual
WHERE NOT EXISTS (SELECT 1 FROM cte)
ORDER BY abc DESC;
Notes:
The value has to be compatible with the type of the column. So, if abc is not a string, then 'Some value' is not appropriate. You haven't provided enough information to determine what value should be in which column.
The ORDER BY should be in the outermost query, not the CTE.
Never use comas in the FROM clause. Always use proper, explicit, standard, readable JOIN syntax.
This version uses meaningful table aliases (table name abbreviations) rather than arbitrary letters.

SQL Complex update query filter distinct values only

I have 3 tables with following columns.
Table: A with column: newColumnTyp1, typ2
Table: B with column: typ2, tableC_id_fk
Table: C with column: id, typ1
I wanted to update values in A.newColumnTyp1 from C.typ1 by following logic:
if A.typ2=B.typ2 and B.tableC_id_fk=C.id
the values must be distinct, if any of the conditions above gives multiple results then should be ignored. For example A.typ2=B.typ2 may give multiple result in that case it should be ignored.
edit:
the values must be distinct, if any of the conditions above gives multiple results then take only one value and ignore rest. For example A.typ2=B.typ2 may give multiple result in that case just take any one value and ignore rest because all the results from A.typ2=B.typ2 will have same B.tableC_id_fk.
I have tried:
SELECT DISTINCT C.typ1, B.typ2
FROM C
LEFT JOIN B ON C.id = B.tableC_id_fk
LEFT JOIN A ON B.typ2= A.typ2
it gives me a result of table with two columns typ1,typ2
My logic was, I will then filter this new table and compare the type2 value with A.typ2 and update A.newColumnTyp1
I thought of something like this but was a failure:
update A set newColumnTyp1= (
SELECT C.typ1 from
SELECT DISTINCT C.typ1, B.typ2
FROM C
LEFT JOIN B ON C.id = B.tableC_id_fk
LEFT JOIN A ON B.typ2= A.type2
where A.typ2=B.typ2);
I am thinking of an updateable CTE and window functions:
with cte as (
select a.newColumnTyp1, c.typ1, count(*) over(partition by a.typ2) cnt
from a
inner join b on b.type2 = a.typ2
inner join c on c.id = b.tableC_id_fk
)
update cte
set newColumnTyp1 = typ1
where cnt > 1
Update: if the columns have the same name, then alias one of them:
with cte as (
select a.typ1, c.typ1 typ1c, count(*) over(partition by a.typ2) cnt
from a
inner join b on b.type2 = a.typ2
inner join c on c.id = b.tableC_id_fk
)
update cte
set typ1 = typ1c
where cnt > 1
I think I would approach this as:
update a
set newColumnTyp1 = bc.min_typ1
from (select b.typ2, min(c.typ1) as min_typ1, max(c.typ1) as max_typ1
from b join
c
on b.tableC_id_fk = c.id
group by b.type2
) bc
where bc.typ2 = a.typ2 and
bc.min_typ1 = bc.max_typ1;
The subquery determines whether typ1 is always the same. If so, it is used for updating.
I should note that you might want the most common value assigned, instead of requiring unanimity. If that is what you want, then you can ask another question.

Using an ALIAS in an INNER JOIN query?

I think the title explain very well my issue, I looked at a lot of subject in forum and else about this but I am still stuck. I don't really understand how to do it, and maybe someone here will be able to make me understand. (I hope)
My query is here:
SELECT Name, A, B, C, D, E, F
FROM Data
INNER JOIN User_Access ON Data.Name = User_Access.Name
WHERE User_Access.Channel = 'TEST'
I know that I have to use an alias for "Name" but I can not do it.
For now, I think my best almost-answer to my issue is this :
SELECT Name AS N, A, B, C, D, E, F
FROM Data
INNER JOIN User_Access ON Data.N = User_Access.Name
WHERE User_Access.Channel = 'TEST'
But it's not working and I have an invalid name error.
Thank's in advance for your help !
SELECT User_Access.Name, A, B, C, D, E, F
FROM Data
INNER JOIN User_Access ON Data.N = User_Access.Name
WHERE User_Access.Channel = 'TEST'
I have replaced the Name (as is key word) with User_Access.Name (changing the column name that is appending with the actual table's name).
Where a column name is common between n tables you need to qualify it so that sql knows which one you mean.This will work
SELECT data.Name, A, B, C, D, E, F
FROM Data
INNER JOIN User_Access ON Data.Name = User_Access.Name
WHERE User_Access.Channel = 'TEST'
as will
SELECT d.Name, A, B, C, D, E, F
FROM Data d
INNER JOIN User_Access ON d.Name = User_Access.Name
WHERE User_Access.Channel = 'TEST'
From Logical Processing Order of the SELECT statement:
The following steps show the logical processing order, or binding
order, for a SELECT statement. This order determines when the objects
defined in one step are made available to the clauses in subsequent
steps. For example, if the query processor can bind to (access) the
tables or views defined in the FROM clause, these objects and their
columns are made available to all subsequent steps. Conversely,
because the SELECT clause is step 8, any column aliases or derived
columns defined in that clause cannot be referenced by preceding
clauses. However, they can be referenced by subsequent clauses such as
the ORDER BY clause. The actual physical execution of the statement is
determined by the query processor and the order may vary from this
list.
FROM
ON
JOIN
WHERE
GROUP BY
WITH CUBE or WITH ROLLUP
HAVING
SELECT
DISTINCT
ORDER BY
TOP
Notice that the SELECT is in position 8, where as JOIN and ON are in positions 3 and 2 respectively. Therefore you cannot reference a column by it's alias defined in the SELECT in the ON; as the SELECT hasn't been processed yet. Effectively, the only place you can reference a column by it's alias (in the same scope) is in the ORDER BY.
If you really wanted to to this, you could use as CROSS APPLY and VALUES operator, but (honestly) you wanting to type N instead of Name in the ON is more laziness:
SELECT V.N, D.A, D.B, D.C, D.D, D.E, D.F --I assume there are all from the Data table
FROM Data D
CROSS APPLY (VALUES(D.[Name])) V(N)
INNER JOIN User_Access UA ON V.N = UA.Name
WHERE UA.Channel = 'TEST';
Name is a reserved word, you need to enclose that in square bracket
SELECT [Name], A, B, C, D, E, F
FROM Data
INNER JOIN User_Access ON Data.N = User_Access.Name
WHERE User_Access.Channel = 'TEST'

Get reference to master select row in nested select

I want my DB to return row under one of two conditions:
The row contains specific value "val" and is of type "sometype"
There is other row, that does have the type "sometype" and the same name as currently checked row.
For this purpose, I made a folowing query:
SELECT c.name, c.value, c.type
FROM `table` AS c
WHERE
c.type='sometype' AND c.value='val'
OR
c.type='othertype' AND
0<(SELECT count(*) FROM `table` AS d WHERE d.name=c.name and c.name='sometype')
Unfortunately, this will return all rows of 'othertype'. I only want these, that have friend with same name and type 'sometype'.
I think the problem is, that I don't know how to get the reference to the values in curently checked row (which I believed to be c.fieldname). I believed d.name = c.name would return all rows that have same name as currently checked row. But the nested select is always >0.
I think this should work for what you are doing:
SELECT c.name, c.type, c.value
FROM test as c
INNER JOIN (SELECT name, type
FROM test
WHERE type = "type1"
AND value = 3) as d
ON c.name = d.name AND c.type = d.type;
SQLFiddle: http://sqlfiddle.com/#!2/63255/1
The inner select gets your #1, where type="sometype" and value="val". Then the outer gets the names, types and values that correspond to the name and type of the inner select, which is your #2.
SELECT c.name
, c.value
, c.type
FROM table c
WHERE (c.type = 'sometype' AND c.value = 'val')
OR (c.type = 'othertype' AND 0< (SELECT COUNT(*) FROM `table` d WHERE d.name = c.name AND c.name='sometype'));
also consider rewriting without the subquery