Pretty simple SQL query using another query - sql

I need to select all IDs from one table with columns ID and X, WHERE X = 'Y'. For each of those IDs, I need to look up some stuff in a different table:
If the ID does not exist, it gets no row in the final result.
If the ID does exist, I want to do some logic to figure out if it gets a row. For simplicity, assume that the logic is: if column Q > 0.
So the final result is simply a column of IDs, throwing out some because they are disqualified for one of two reasons as above.
thanks.

This is what JOINs are made for.
SELECT table1.* FROM table1
INNER JOIN table2
ON table1.ID = table2.table1_ID
AND table2.Q > 0;
This will select all records in table1 (which have IDs) and then remove any records that do not have a matching record in table2 or do not have a Q > 0.

If I got your question right this might be what you are looking for:
select id from your_table
where X = 'Y'
and id in (select id from other_table where Q > 0)

You can use a subquery:
SELECT id FROM table1 WHERE table1.id IN (SELECT q FROM table2 WHERE table2.q > 0) AND table1.x='y'
SQL will check to see if the results from the outermost query are in the subquery (the part in parentheses) and won't return anything if they aren't.

Related

Create a new column based on an existing column in SQL

I have a question in SQL. I have an existing data table like following:
And would like to make the selection into a list. The rule is as following: if an ID selects a product, we will provide it a value 1. Otherwise, the value is 0. For example, ID 1 choose product A and B. So the corresponding is_tap for product A and B is 1, and for C is 0, since there is no selection. The selection here is limited and known. The final table will be something like:
How can I generate this table in SQL? Thanks in advance for your help.
Have derived tables (the subqueries) producing all possible combinations of id and selection.
LEFT JOIN the original table. Use a case expression to return 1 if row exists, otherwise 0.
select t2.id, t3.selection, case when t1.id is not null then 1 else 0 end
from (select distinct id from t1) t2
cross join (select distinct selection from t1) t3
left join t1 on t2.id = t1.id and t3.selection = t1.selection
order by t2.id, t3.selection
Demo at https://dbfiddle.uk/-sS-bvxM

Cross joining tables to see which partners in one table have a report from another table [duplicate]

table1 (id, name)
table2 (id, name)
Query:
SELECT name
FROM table2
-- that are not in table1 already
SELECT t1.name
FROM table1 t1
LEFT JOIN table2 t2 ON t2.name = t1.name
WHERE t2.name IS NULL
Q: What is happening here?
A: Conceptually, we select all rows from table1 and for each row we attempt to find a row in table2 with the same value for the name column. If there is no such row, we just leave the table2 portion of our result empty for that row. Then we constrain our selection by picking only those rows in the result where the matching row does not exist. Finally, We ignore all fields from our result except for the name column (the one we are sure that exists, from table1).
While it may not be the most performant method possible in all cases, it should work in basically every database engine ever that attempts to implement ANSI 92 SQL
You can either do
SELECT name
FROM table2
WHERE name NOT IN
(SELECT name
FROM table1)
or
SELECT name
FROM table2
WHERE NOT EXISTS
(SELECT *
FROM table1
WHERE table1.name = table2.name)
See this question for 3 techniques to accomplish this
I don't have enough rep points to vote up froadie's answer. But I have to disagree with the comments on Kris's answer. The following answer:
SELECT name
FROM table2
WHERE name NOT IN
(SELECT name
FROM table1)
Is FAR more efficient in practice. I don't know why, but I'm running it against 800k+ records and the difference is tremendous with the advantage given to the 2nd answer posted above. Just my $0.02.
SELECT <column_list>
FROM TABLEA a
LEFTJOIN TABLEB b
ON a.Key = b.Key
WHERE b.Key IS NULL;
https://www.cloudways.com/blog/how-to-join-two-tables-mysql/
This is pure set theory which you can achieve with the minus operation.
select id, name from table1
minus
select id, name from table2
Here's what worked best for me.
SELECT *
FROM #T1
EXCEPT
SELECT a.*
FROM #T1 a
JOIN #T2 b ON a.ID = b.ID
This was more than twice as fast as any other method I tried.
Watch out for pitfalls. If the field Name in Table1 contain Nulls you are in for surprises.
Better is:
SELECT name
FROM table2
WHERE name NOT IN
(SELECT ISNULL(name ,'')
FROM table1)
You can use EXCEPT in mssql or MINUS in oracle, they are identical according to :
http://blog.sqlauthority.com/2008/08/07/sql-server-except-clause-in-sql-server-is-similar-to-minus-clause-in-oracle/
That work sharp for me
SELECT *
FROM [dbo].[table1] t1
LEFT JOIN [dbo].[table2] t2 ON t1.[t1_ID] = t2.[t2_ID]
WHERE t2.[t2_ID] IS NULL
You can use following query structure :
SELECT t1.name FROM table1 t1 JOIN table2 t2 ON t2.fk_id != t1.id;
table1 :
id
name
1
Amit
2
Sagar
table2 :
id
fk_id
email
1
1
amit#ma.com
Output:
name
Sagar
All the above queries are incredibly slow on big tables. A change of strategy is needed. Here there is the code I used for a DB of mine, you can transliterate changing the fields and table names.
This is the strategy: you create two implicit temporary tables and make a union of them.
The first temporary table comes from a selection of all the rows of the first original table the fields of which you wanna control that are NOT present in the second original table.
The second implicit temporary table contains all the rows of the two original tables that have a match on identical values of the column/field you wanna control.
The result of the union is a table that has more than one row with the same control field value in case there is a match for that value on the two original tables (one coming from the first select, the second coming from the second select) and just one row with the control column value in case of the value of the first original table not matching any value of the second original table.
You group and count. When the count is 1 there is not match and, finally, you select just the rows with the count equal to 1.
Seems not elegant, but it is orders of magnitude faster than all the above solutions.
IMPORTANT NOTE: enable the INDEX on the columns to be checked.
SELECT name, source, id
FROM
(
SELECT name, "active_ingredients" as source, active_ingredients.id as id
FROM active_ingredients
UNION ALL
SELECT active_ingredients.name as name, "UNII_database" as source, temp_active_ingredients_aliases.id as id
FROM active_ingredients
INNER JOIN temp_active_ingredients_aliases ON temp_active_ingredients_aliases.alias_name = active_ingredients.name
) tbl
GROUP BY name
HAVING count(*) = 1
ORDER BY name
See query:
SELECT * FROM Table1 WHERE
id NOT IN (SELECT
e.id
FROM
Table1 e
INNER JOIN
Table2 s ON e.id = s.id);
Conceptually would be: Fetching the matching records in subquery and then in main query fetching the records which are not in subquery.
First define alias of table like t1 and t2.
After that get record of second table.
After that match that record using where condition:
SELECT name FROM table2 as t2
WHERE NOT EXISTS (SELECT * FROM table1 as t1 WHERE t1.name = t2.name)
I'm going to repost (since I'm not cool enough yet to comment) in the correct answer....in case anyone else thought it needed better explaining.
SELECT temp_table_1.name
FROM original_table_1 temp_table_1
LEFT JOIN original_table_2 temp_table_2 ON temp_table_2.name = temp_table_1.name
WHERE temp_table_2.name IS NULL
And I've seen syntax in FROM needing commas between table names in mySQL but in sqlLite it seemed to prefer the space.
The bottom line is when you use bad variable names it leaves questions. My variables should make more sense. And someone should explain why we need a comma or no comma.
I tried all solutions above but they did not work in my case. The following query worked for me.
SELECT NAME
FROM table_1
WHERE NAME NOT IN
(SELECT a.NAME
FROM table_1 AS a
LEFT JOIN table_2 AS b
ON a.NAME = b.NAME
WHERE any further condition);

How to delete row with just one column duplicated in sql?

This is my records.
I just want to delete records with
PACK = '1`S'
but if the row does not have any duplicates even if its PACK is equals to '1`S' it will remain.
The only thing I can do is to check the records that have duplicates.
And to delete records with PACK = 1's.
SELECT Name, COUNT(*) as duplicates
FROM table1
Group by Name
having COUNT(*) >1
Order by duplicates
Note: I just replace delete with select.
SELECT * FROM table1 WHERE PACK = '1's'
So you want to delete any 1`S records for which exists a record with the same name and category and another pack. You can word exactly this in SQL:
delete from table1
where pack = '1`S'
and exists
(
select *
from table1 other
where other.name = table1.name
and other.category = table1.category
and other.pack <> table1.pack
);
This is standard SQL and should work in about every DBMS. With MySQL being the notorious exception, as they forbid to directly use the updated table in a subquery again. Here you'd have to replace from table1 other by from (select * from table1) other.
Maybe this pl/sql can guide you:
Delete from table1
where id not in (select min(id)
from table1
where pack = '1`S'
group by name
having count(*) = 1
)
and pack = '1`S';
INNER JOIN the table on itself with the restriction that the ID field is larger than the ID field found by the join. If there are no duplicates there will not be any results. If there are duplicates the most recently added will be deleted.

SQL query join question

How do you do SQL query for the following condition? Suppose you have two tables: table1 and table2, where each entry in table1 can have multiple corresponding entries in table2. The pseudo code for the query that I want is:
for each $row in table1
$rows = find all rows in table2 that corresponds to $row with $row.id == table2.foreign_id
# $rows is an array of corresponding row in table2
if all rows in $rows meet some condition
then
return $row
else
continue
end
end
EDIT: note in the above pseudo code, I only want the row in table1 that has all its relations in TABLE2 that meets some conditions, not just some condition in table1.
PS: I want to do it in SQL due to efficiency problems that I may have otherwise.
Thanks a lot.
You can reformulate this with a where not exists ( .. ) type clause.
For example, pretending you want a list of customers whose orders are all completed:
select * from customers c
where not exists (
select * from orders
where customerid=c.id
and status <> 'C'
)
So you are asking for all customers who have no uncompleted orders - which is the same thing as all customers whose orders are all completed.
Instead of:
if all rows in $rows meet some condition
You are saying:
if NO rows in $rows DO NOT meet some condition
Edit: As pointed out in the comments, this will also return customers who have no orders. You could add and exists (select * from orders where customerid=c.id) to the end of the above to exclude these rows.
select * from table1 as t1
inner join table2 as t2
on t1.id == t2.foreign_id
where -- some condition goes here
This query will only return the rows from table1 that have a match in table2 and that match the where clause.
I would suggest checking out SQLCourse - Interactive Online SQL Training for Beginners since this really is a basic SQL query.
As ck mentioned, this is really basic sql.
for each $row in table1
SELECT table1.* FROM table1
find all rows in table2 that corresponds to $row with $row.id == table2.foreign_id
LEFT JOIN table2 ON table1.id = table2.foreign_id
if all rows in $rows meet some condition
WHERE condition_here
The entire SQL becomes
SELECT
table1.*
FROM table1
LEFT JOIN table2 ON table1.id = table2.foreign_id
WHERE condition_here
I think this is what you are getting at... The nested select is a derived table named sub_query and corresponds to this part of you pseudo code ($rows = find all rows in table2 that corresponds to $row with $row.id == table2.foreign_id). The outer select lets you further filter the first part of your pseudo code by some condition (your if statement)
select
sub_query.*
from
(select
*
from
table1,
table2
where
table1.id = table2.foreign_key_id) sub_query
where
sub_query.some_field = "some condition"
Enjoy!
Here's a possible solution. I use Oracle, not sure if the syntax is exactly right for MySQL, but I imagine you can work up an equivalent.
The idea of this is to find all ids in table2 for which all rows meet the desired condition, then look up those ids in table1.
SELECT *
FROM table1
WHERE id IN (
SELECT id
FROM table2
GROUP BY id
HAVING COUNT(*) = SUM( CASE WHEN <somecondition> THEN 1 ELSE 0 END )
)
general format is:
SELECT *
FROM Table1 t1
INNER JOIN Table2 t2 ON (t1.ID = t2.ID)
WHERE ...

Getting distinct rows from a left outer join

I am building an application which dynamically generates sql to search for rows of a particular Table (this is the main domain class, like an Employee).
There are three tables Table1, Table2 and Table1Table2Map.
Table1 has a many to many relationship with Table2, and is mapped through Table1Table2Map table. But since Table1 is my main table the relationship is virtually like a one to many.
My app generates a sql which basically gives a result set containing rows from all these tables. The select clause and joins dont change whereas the where clause is generated based on user interaction. In any case I dont want duplicate rows of Table1 in my result set as it is the main table for result display. Right now the query that is getting generated is like this:
select distinct Table1.Id as Id, Table1.Name, Table2.Description from Table1
left outer join Table1Table2Map on (Table1Table2Map.Table1Id = Table1.Id)
left outer join Table2 on (Table2.Id = Table1Table2Map.Table2Id)
For simplicity I have excluded the where clause. The problem is when there are multiple rows in Table2 for Table1 even though I have said distinct of Table1.Id the result set has duplicate rows of Table1 as it has to select all the matching rows in Table2.
To elaborate more, consider that for a row in Table1 with Id = 1 there are two rows in Table1Table2Map (1, 1) and (1, 2) mapping Table1 to two rows in Table2 with ids 1, 2. The above mentioned query returns duplicate rows for this case. Now I want the query to return Table1 row with Id 1 only once. This is because there is only one row in Table2 that is like an active value for the corresponding entry in Table1 (this information is in Mapping table).
Is there a way I can avoid getting duplicate rows of Table1.
I think there is some basic problem in the way I am trying to solve the problem, but I am not able to find out what it is. Thanks in advance.
Try:
left outer join (select distinct YOUR_COLUMNS_HERE ...) SUBQUERY_ALIAS on ...
In other words, don't join directly against the table, join against a sub-query that limits the rows you join against.
You can use GROUP BY on Table1.Id ,and that will get rid off the extra rows. You wouldn't need to worry about any mechanics on join side.
I came up with this solution in a huge query and it this solution didnt effect the query time much.
NOTE : I'm answering this question 3 years after its been asked but this may help someone i believe.
You can re-write your left joins to be outer applies, so that you can use a top 1 and an order by as follows:
select Table1.Id as Id, Table1.Name, Table2.Description
from Table1
outer apply (
select top 1 *
from Table1Table2Map
where (Table1Table2Map.Table1Id = Table1.Id) and Table1Table2Map.IsActive = 1
order by somethingCol
) t1t2
outer apply (
select top 1 *
from Table2
where (Table2.Id = Table1Table2Map.Table2Id)
) t2;
Note that an outer apply without a "top" or an "order by" is exactly equivalent to a left outer join, it just gives you a little more control. (cross apply is equivalent to an inner join).
You can also do something similar using the row_number() function:
select * from (
select distinct Table1.Id as Id, Table1.Name, Table2.Description,
rowNum = row_number() over ( partition by table1.id order by something )
from Table1
left outer join Table1Table2Map on (Table1Table2Map.Table1Id = Table1.Id)
left outer join Table2 on (Table2.Id = Table1Table2Map.Table2Id)
) x
where rowNum = 1;
Most of this doesn't apply if the IsActive flag can narrow down your other tables to one row, but they might come in useful for you.
To elaborate on one point: you said that there is only one "active" row in Table2 per row in Table1. Is that row not marked as active such that you could put it in the where clause? Or is there some magic in the dynamic conditions supplied by the user that determines what's active and what isn't.
If you don't need to select anything from Table2 the solution is relatively simply in that you can use the EXISTS function but since you've put TAble2.Description in the clause I'll assume that's not the case.
Basically what separates the relevant rows in Table2 from the irrelevant ones? Is it an active flag or a dynamic condition? The first row? That's really how you should be removing duplicates.
DISTINCT clauses tend to be overused. That may not be the case here but it sounds like it's possible that you're trying to hack out the results you want with DISTINCT rather than solving the real problem, which is a fairly common problem.
You have to include activity clause into your join (and no need for distinct):
select Table1.Id as Id, Table1.Name, Table2.Description from Table1
left outer join Table1Table2Map on (Table1Table2Map.Table1Id = Table1.Id) and Table1Table2Map.IsActive = 1
left outer join Table2 on (Table2.Id = Table1Table2Map.Table2Id)
If you want to display multiple rows from table2 you will have duplicate data from table1 displayed. If you wanted to you could use an aggregate function (IE Max, Min) on table2, this would eliminate the duplicate rows from table1, but would also hide some of the data from table2.
See also my answer on question #70161 for additional explanation