SQL Query for persons liking both fruits and vegetables [closed] - sql

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 9 months ago.
Improve this question
I have a situation I am trying to resolve. Might be simple but I can't figure it out. I have simplified my table that I am trying to pull data from. For this example I am using 2 columns, (persons and Fruits_vegetables). I am trying to filter only the person the like both fruits and vegetables. Please guide me.
Persons Fruits_Vegetables
Michael Apples
Michael Oranges
Michael Bell Peppers
Maria Carrots
Maria Mangoes
Maria Bananas
Brenda Bananas
Brenda Mangoes
Brenda Peaches
Alina Mangoes
Alina Grapes
Alina Peaches
Nicole Carrots
Nicole Bell Peppers
Nicole Lettuce
Christian Carrots
Christian Bananas

Lots of ways:
select distinct t1.Persons
from T t1 inner join T t2 on t2.Persons = t1.Persons
where t1.Fruits_Vegetables in ('Apples', 'Oranges', 'Mangoes',
'Bananas', 'Peaches', 'Grapes')
and t2.Fruits_Vegetables in ('Bell Peppers', 'Carrots', 'Lettuce')
I'm guessing this is a class exercise. Knowing that there's only a single person who can match across only two categories does make it easier to use a join approach. The problem is a bit contrived though it does apply concepts of joins, self-joins, many-to-many joins as well as using table aliases, in and distinct and possibly helping you to understand why combining the two values into a single column becomes problematic.
A more advanced technique might be:
with data as (
select *,
case when Fruits_Vegetables in (
'Apples', 'Oranges', 'Mangoes', 'Bananas', 'Peaches', 'Grapes'
) then 'F'
when Fruits_Vegetables in (
'Bell Peppers', 'Carrots', 'Lettuce'
) then 'V' end as category
from T
)
select Persons
from data
group by Persons
having count(distinct category) > 1

you need to have some idea about a master data list of all fruits and all vegetables so that the data in the column fruit_vegetables can be classified as fruit or vegetable.
Once you do that, you can use a query like below
select distinct Persons
from yourtable t1
where
exists
(select 1 from yourtable t2
where t1.Persons =t2.Persons
and t2.Fruits_Vegetables in ('Bell Peppers', 'Carrots')-- master list of veg
)
and
(select 1 from yourtable t2
where t1.Persons =t2.Persons
and t2.Fruits_Vegetables in ('Apples', 'Bananas')-- master list of fruits
)

Related

Select if distinct count > 2?

[EDITED 10/03/2019 as per request]
Have a large dataset and need to map 2 ids, on different rows, to one 'conflict id'.
For example.
These rows must be attributed to a single value, "Apples & Pears".
I then need to know which persons have both "Apple" and "Pears", through this value, not just one of then.
It's a many to one relationship, that must be complete. Users must not be attributed "Apples & Pears“ if they do not have BOTH requirements.
I have master table of one to one relation for each fruit to each combination, but only want them to be joined with user table if the user has both fruits.
Table 1: User, Fruit
Josh, Apple
Josh, Pear
Tom, Apple
Kate, Pear
Table 2: Fruit, Product
Apple, Apples&Pears
Pear, Apples&Pears
Table 3 (OUTCOME I WANT TO ACHIEVE):
Josh, Apple, Apples&Pears
Josh, Pear, Apples&Pears
Tom, Apple, NULL
Kate, Pear, NULL
Welcome to S/O. Typically you want to provide more realistic sample table structures and data to portray at least critical to your question. if ever private data, make up, but keep it in accurate context.
That being said, I don't think you actually have a table of apples, pears and apples & pears, but I will play along.
I assume (example) you have a table of people, and another table of things they are associated with in a child table. I would do a join per person FIRST, to those that DO have an association with the "apples & pears" record, then do a LEFT-JOIN to same child table once for apples, another for pears. You can determine what you want to do with results (such as delete from the individual apples, pears child records).
Select
p.LastName,
p.FirstName,
case when justApples.PersonID IS NULL
then 'No' else 'Yes' end as AlsoHasApples,
case when justPears.PersonID IS NULL
then 'No' else 'Yes' end as AlsoHasPears
from
Person p
JOIN Fruits f
on p.PersonID = f.personID
AND f.Description = 'Apples & Pears'
LEFT JOIN Fruits justApples
on p.PersonID = justApples.PersonID
AND f.Description = 'Apples'
LEFT JOIN Fruits justPears
on p.PersonID = justPears.PersonID
AND f.Description = 'Pears'
Since the first "(inner) JOIN" explicitly is looking for those that ARE marked as both apples & pears record, that minimizes the list to just those people pre-qualified.
THEN, the two "LEFT-JOIN" are explicitly looking for the OTHER individualized "Apples" and "Pears" respectively.
By testing the IS NULL will indicate if the person does (or not via NULL) have that item.
If you ONLY WANT to return those that are Apples & Pears AND have one or both of the other individual items, you can add a WHERE clause at the end such as
WHERE
justApples.PersonID IS NOT NULL
OR justPears.PersonID IS NOT NULL
An inverse would be to pre-qualify the justApples and justPears and NOT marked as associated with Apples & Pears such as by REQUIRING the JOINS to apples and pears individually and LEFT-JOIN the combination record
Select
p.LastName,
p.FirstName,
case when f.PersonID IS NULL
then 'No' else 'Yes' end as IsAssociatedWithApplesAndPearsRecord
from
Person p
JOIN Fruits justApples
on p.PersonID = justApples.PersonID
AND f.Description = 'Apples'
JOIN Fruits justPears
on p.PersonID = justPears.PersonID
AND f.Description = 'Pears'
LEFT JOIN Fruits f
on p.PersonID = f.personID
AND f.Description = 'Apples & Pears'
The two joins to the individual REQUIRE each individual part, and the LEFT-JOIN shows IF they are also marked as the combination entry. As you can see by the ambiguity in your question, providing a little more detail in what you want can significantly help getting a more accurate answer to your true needs. Try not to mask details to context, but mask the sample data in itself where more private issues are of concern.

SQL with Bob and John owing each other money

I have got the following 3 fields in a file: person_ows person_is_owed amount
Example content:
Bob John 100
John Bob 110
What does a SQL look like that produces:
Bob John 100 110
John Bob 110 100
Sorry if this is a trivial question, but I am just trying to learn SQL and I find it really like HELL!
So, what you need is to be able to JOIN two rows. In this case you'll probably want an OUTER JOIN assuming that there isn't always a match of each owing the other. Now you just need to come up with your JOIN criteria, which in this case is going to be based on the names (person_owes and person_is_owed):
SELECT
T1.person_owes,
T1.person_is_owed,
T1.amount AS owes_amount,
COALESCE(T2.amount, 0) AS is_owed_amount
FROM
My_Table T1
LEFT OUTER JOIN My_Table T2 ON T2.person_is_owed = T1.person_owes
The COALESCE is just to make sure that when there is no match that you get a value of 0 instead of NULL.
Also, this assumes that there is only going to be one of each combination of person_owes and person_is_owed. If you might have two rows showing that John owes Bill two different amounts of money then you would have to adjust the SQL above and it would be a bit more complex.
If you plan to use SQL much then you should invest the time in reading one (or preferably more) beginning books on the subject.
Assuming that the combination of (person_ows, person_is_owed) is unique
select person_ows,
person_is_owed,
amount,
(select t2.amount
from the_table t2
where (t2.person_ows, t2.person_is_owed) = (t1.person_is_owed, t1.person_ows))
from the_table t1

SQL change columns to row but keep one column as the column before all the rows

Example
Team | Person1 | Person2 | Person3
change to
Team | Person 1
Team | Person 2
Team | Person 3
I wasn't sure how to describe exactly what it was that I wanted to do, sorry if this is a duplicate.
Use UNPIVOT
For table dbo.Teams, using your example:
SELECT Team, Person
FROM
(
SELECT Team, Person1, Person2, Person3
FROM dbo.Teams
) as cp
UNPIVOT
(
Person FOR Persons IN (Person1, Person2, Person3)
) AS up;
While using UNIONs works, it does not scale well, costing a lot more for each additional column you need to transpose.
NOTE: This is basically a clone of the solution to a near identical question at this article, provided in the initial question comments by user #Goat CO. See the link for additional information and expansion on this solution.
I think you might try (besides the the solution user #GoatCO mentioned in the comments) the following query:
SELECT Team, [Person 1] As Person_Column
FROM _table
UNION ALL
SELECT Team, [Person 2] As Person_Column
FROM _table
UNION ALL
SELECT Team, [Person 3] As Person_Column
FROM _table

SSRS query and WHERE with multiple

Being new with SQL and SSRS and can do many things already, but I think I must be missing some basics and therefore bang my head on the wall all the time.
A report that is almost working, needs to have more results in it, based on conditions.
My working query so far is like this:
SELECT projects.project_number, project_phases.project_phase_id, project_phases.project_phase_number, project_phases.project_phase_header, project_phase_expensegroups.projectphase_expense_total, invoicerows.invoicerow_total
FROM projects INNER JOIN
project_phases ON projects.project_id = project_phases.project_id
LEFT OUTER JOIN
project_phase_expensegroups ON project_phases.project_phase_id = project_phase_expensegroups.project_phase_id
LEFT OUTER JOIN
invoicerows ON project_phases.project_phase_id = invoicerows.project_phase_id
WHERE ( projects.project_number = #iProjectNumber )
AND
( project_phase_expensegroups.projectphase_expense_total >0 )
The parameter is for selectionlist that is used to choose a project to the report.
How to have also records that have
( project_phase_expensegroups.projectphase_expense_total ) with value 0 but there might be invoices for that project phase?
Tried already to add another condition like this:
WHERE ( projects.project_number = #iProjectNumber )
AND
( project_phase_expensegroups.projectphase_expense_total > 0 )
OR
( invoicerows.invoicerow_total > 0 )
but while it gives some results - also the one with projectphase_expense_total with value 0, but the report is total mess.
So my question is: what am I doing wrong here?
There is a core problem with your query in that you are left joining to two tables, implying that rows may not exist, but then putting conditions on those tables, which will eliminate NULLs. That means your query is internally inconsistent as is.
The next problem is that you're joining two tables to project_phases that both may have multiple rows. Since these data are not related to each other (as proven by the fact that you have no join condition between project_phase_expensegroups and invoicerows, your query is not going to work correctly. For example, given a list of people, a list of those people's favorite foods, and a list of their favorite colors like so:
People
Person
------
Joe
Mary
FavoriteFoods
Person Food
------ ---------
Joe Broccoli
Joe Bananas
Mary Chocolate
Mary Cake
FavoriteColors
Person Color
------ ----------
Joe Red
Joe Blue
Mary Periwinkle
Mary Fuchsia
When you join these with links between Person <-> Food and Person <-> Color, you'll get a result like this:
Person Food Color
------ --------- ----------
Joe Broccoli Red
Joe Bananas Red
Joe Broccoli Blue
Joe Bananas Blue
Mary Chocolate Periwinkle
Mary Chocolate Fuchsia
Mary Cake Periwinkle
Mary Cake Fuchsia
This is essentially a cross-join, also known as a Cartesian product, between the Foods and the Colors, because they have a many-to-one relationship with each person, but no relationship with each other.
There are a few ways to deal with this in the report.
Create ExpenseGroup and InvoiceRow subreports, that are called from the main report by a combination of project_id and project_phase_id parameters.
Summarize one or the other set of data into a single value. For example, you could sum the invoice rows. Or, you could concatenate the expense groups into a single string separated by commas.
Some notes:
Please, please format your query before posting it in a question. It is almost impossible to read when not formatted. It seems pretty clear that you're using a GUI to create the query, but do us the favor of not having to format it ourselves just to help you
While formatting, please use aliases, Don't use full table names. It just makes the query that much harder to understand.
You need an extra parentheses in your where clause in order to get the logic right.
WHERE ( projects.project_number = #iProjectNumber )
AND (
(project_phase_expensegroups.projectphase_expense_total > 0)
OR
(invoicerows.invoicerow_total > 0)
)
Also, you're using a column in your WHERE clause from a table that is left joined without checking for NULLs. That basically makes it a (slow) inner join. If you want to include rows that don't match from that table you also need to check for NULL. Any other comparison besides IS NULL will always be false for NULL values. See this page for more information about SQL's three value predicate logic: http://www.firstsql.com/idefend3.htm
To keep your LEFT JOINs working as you intended you would need to do this:
WHERE ( projects.project_number = #iProjectNumber )
AND (
project_phase_expensegroups.projectphase_expense_total > 0
OR project_phase_expensegroups.project_phase_id IS NULL
OR invoicerows.invoicerow_total > 0
OR invoicerows.project_phase_id IS NULL
)
I found the solution and it was kind easy after all. I changed the only the second LEFT OUTER JOIN to INNER JOIN and left away condition where the query got only results over zero. Also I used SELECT DISTINCT
Now my report is working perfectly.

How can I avoid using multiple SQL calls to get data?

I have a MySQL database, in which I have a table of monkeys:
id name
1 Alice
2 Bill
3 Donkey Kong
4 Edna
5 Feefee
I also have a table of bananas and where they were picked from.
id where_from
1 USA
2 Botswana
3 Banana-land
4 USA
Finally, I have a table matches that describes which bananas belong to which monkeys. Each monkey can only have one banana, and no monkeys can share a banana. Some monkeys may lack a banana.
id monkey_id banana_id
1 3 4
2 4 1
3 5 2
How can I use a single SQL statement to retrieve all the matches? For each match, I want the name of the monkey as well as where the banana is from.
I have tried the following 3 SQL statements, which work:
SELECT * FROM matches
SELECT * FROM monkeys WHERE id=[monkey_id from 1st SQL query]
SELECT * FROM bananas WHERE id=[banana_id from 1st SQL query]
I feel that 3 SQL statements is cumbersome though. Any ideas on how I can just use a single SQL statement? I am just learning SQL and am monkeying around with the basics. Thanks!
Since some monkeys may lack a banana, that implies a LEFT JOIN between matches and monkeys. That will ensure all monkeys are listed, even if they have no bananas in matches.
SELECT
monkeys.name,
bananas.where_from
FROM
monkeys
/* List all monkeys, even if they have no match */
LEFT JOIN matches ON monkeys.id = matches.monkey_id
/* And another LEFT JOIN to link matches to bananas */
LEFT JOIN bananas ON bananas.id = matches.banana_id
Here is an example on SQLfiddle.com
I very highly recommend reading over Jeff Atwood's (co-founder of Stack Overflow) excellent article explaining SQL joins.