Find a value from table that is only unique to one id - sql

I have a table "stats" that consists 3 ids.
IDs: id_seller, id_part and id_proj
From this table, I want to find projects (id_proj) which buy specific parts (id_part) that is avaiable only from one seller.
In other words: Find id_proj, which buy id_parts, which are avaiable only from one seller (seller S5 is the only seller that sells P2).
So, In this example id_part (P2) is the only part id, that is specific and it is selling just to id_seller (S5).
The return should be: J2, J4
I ve tried with something like this:
SELECT DISTINCT s.id_proj
FROM stats s
WHERE NOT id_part IN (
SELECT s2.id_part
FROM stats s2
WHERE s2.id_seller = 'S5');

Use Group By with Having . Group by id_part and select pieces that have a seller.
Then join the result to the main table to get the information you need.
SELECT s2.*
FROM
(SELECT id_part,max(id_seller) AS id_seller
FROM stats
GROUP BY id_part
HAVING COUNT(DISTINCT id_seller) = 1) s1
JOIN stats s2 ON s1.id_part = s2.id_part AND s1.id_seller = s2.id_seller

select distinct id_proj from stats
where id_part in
(SELECT s.id_part
FROM stats s
WHERE s.id_seller = 'S5'
and id_part not in (select id_part from stats where id_seller <> 'S5') --**this take only part from seller S5**
)
if this is a homework though you should really understand the concept of IN and NOT IN like in this thread, EXIST and NOT EXIST.

Related

SQL Selecting & Counting In the same query

thanks in advance for any help on this, I am a bit of a newbie to MS SQL and I want to do something that I think is achievable but don't have the know how.
I have a simple table called "suppliers" where I can do (SELECT id, name FROM suppliers ORDER BY id ASC)
id
name
1
ACME
2
First Stop Business Supplies
3
All in One Supply Warehouse
4
Farm First Supplies
I have another table called "products"
id
name
supplier_id
1
Item 1
2
2
Item 2
1
3
Item 3
1
4
Item 4
3
5
Item 5
2
I want to list all the suppliers and get the total amount of products for each supplier if that makes sense on the same row? I am just not sure how to pass the suppliers.id through the query to get the count.
I am hoping to get to this:
id
name
total_products
1
ACME
2
2
First Stop Business Supplies
2
3
All in One Supply Warehouse
1
4
Farm First Supplies
0
I really appreciate any help on this.
Three concepts to grasp here. Left Join, group by, and Count().
select s.id, s.name, Count(*) as total_products
from suppliers s
left join products p on s.id=p.supplier_id --the left join gets your no matches
group by s.id, s.name
left join is a join where all of the values from the first table are kept even if there are no matches in the second.
Group by is an aggregation tool where the columns to be aggregated are entered.
Count() is simply a count of transactions for the grouped columns.
Try this :-
SELECT id, name, C.total_products
FROM Suppliers S
OUTER APPLY (
SELECT Count(id) AS total_products
FROM Products P
WHERE P.supplier_id = S.id
) C

Union Three or more tables with conditions

I need a help to solve some problem.
I have some table levelAsignment with columns level_id, store_id and user_id. For each user_id I can write a query to get his level_ids and store_ids.
Also I have a table stores.
I need to get for each store his level and count the users of the current level and store.
It's easy, but the problem is in storing data, Because in the levelAsignment table the user can set all stores for some operator level.
It looks like this:
level_id | store_Id | user_id
4 1 5
1 5 5
6 1
when store_id = 1 in the stores table it means all stores, so I need to show all stores except 1.
select * from stores where id != 1;
so I need an advice how to organize that.
I find different ways to solve the problem, but there were many unions and conditions.
This depends on how you are able to join the stores table
I think you should join level_assignment (where the store_id = 1) with all data in the stores table, but subquery where the outer query excludes the store_id = 1 column from the level assignment table. You may have to create a join column in temporary tables for the stores data. Then union the level_assignment table where store_id != 1
Example:
WITH get_all_stores_for_store_id_1 AS (
SELECT
a.level_assigment,
a.store_id,
b.store_id,
a.user_id
FROM level_assignment a
LEFT JOIN stores b ON a.join_column = b.join_column
WHERE a.store_id = 1)
SELECT
level_assignment,
b.store_id AS store_id,
user_id
FROM get_all_stores_for_store_id_1
UNION
SELECT
level_assignment,
store_id,
user_id
FROM level_assignment
WHERE store_id != 1
Does that make sense?
Thinking about how to join the data, we could do something like this:
Get the stores table and create a 1 column with a one in every row for the stores, so that we can then join all stores to the level_assignment table with store_id = 1:
WITH set_1_column_in_stores_table AS (
SELECT
1 AS join_id,
store_id,
FROM stores),
all_store_rows_get_all_stores AS (
SELECT
a.level_assigment,
a.store_id,
b.store_id,
a.user_id
FROM level_assignment a
LEFT JOIN set_1_column_in_stores_table b ON a.store_id= b.join_id
-- The above will join all stores where store_id = 1 in level_assigment
WHERE a.store_id = 1)
SELECT
level_assignment,
b.store_id AS store_id,
user_id
FROM all_store_rows_get_all_stores
UNION
SELECT
level_assignment,
store_id,
user_id
FROM level_assignment
WHERE store_id != 1

Select all customers loyal to one company?

I've got tables:
TABLE | COLUMNS
----------+----------------------------------
CUSTOMER | C_ID, C_NAME, C_ADDRESS
SHOP | S_ID, S_NAME, S_ADDRESS, S_COMPANY
ORDER | S_ID, C_ID, O_DATE
I want to select id of all customers who made order only from shops of one company - 'Samsung' ('LG', 'HP', ... doesn't really matter, it's dynamic).
I've come only with one solution, but I consider it ugly:
( SELECT DISTINCT c_id FROM order JOIN shop USING(s_id) WHERE s_company = "Samsung" )
EXCEPT
( SELECT DISTINCT c_id FROM order JOIN shop USING(s_id) WHERE s_company != "Samsung" );
Same SQL queries, but reversed operator. Isn't there any aggregate method which solves such query better?
I mean, there could be millions of orders(I don't really have orders, I've got something that occurs more often).
Is it efficient to select thousands of orders and then compare them to hundreds of thousands orders which have different company? I know, that it compares sorted things, so it's O( m + n + sort(n) + sort(m) ). But that's still large for millions of records, or isn't?
And one more question. How could I select all customer values (name, address). How can I join them, can I do just
SELECT CUSTOMER.* FROM CUSTOMER JOIN ( (SELECT...) EXCEPT (SELECT...) ) USING (C_ID);
Disclaimer: This question ain't homework. It's preparation for the exam and desire to things more effective. My solution would be accepted at exam, but I like effective programming.
I like to approach this type of question using group by and a having clause. You can get the list of customers using:
select o.c_id
from orders o join
shops s
on o.s_id = o.s_id
group by c_id
having min(s.s_company) = max(s.s_company);
If you care about the particular company, then:
having min(s.s_company) = max(s.s_company) and
max(s.s_company) = 'Samsung'
If you want full customer information, you can join the customers table back in.
Whether this works better than the except version is something that would have to be tested on your system.
How about a query that uses no aggregate functions like Min and Max?
select C_ID, S_ID
from shop
group by C_ID, S_ID;
Now we have a distinct list of customers and all the companies they shopped at. The loyal customers will be the ones who only appear once in the list.
select C_ID
from Q1
group by C_ID
having count(*) = 1;
Join back to the first query to get the company id:
with
Q1 as(
select C_ID, S_ID
from shop
group by C_ID, S_ID
),
Q2 as(
select C_ID
from Q1
group by C_ID
having count(*) = 1
)
select Q1.C_ID, Q1.S_ID
from Q1
join Q2
on Q2.C_ID = Q1.C_ID;
Now you have a list of loyal customers and the one company each is loyal to.

Help construct a query given a schema

Here is the schema for the database: http://i.stack.imgur.com/omX60.png
Question is: How many people have at least five еntitlements?
I've got this, please tell me how wrong it is and fix it.
select count(personId)
from serialNumber_tbl natural join entitlement_tbl
group by personId
having sum(entitlementID) > 5
Thank you.
The condition for at least 5 is >= 5, not > 5
You need to count the distinct ids in the entitlement table, not person
This gives you the persons, next you need to subquery it to find the count of persons.
select count(personId)
FROM
(
select personId
from serialNumber_tbl natural join entitlement_tbl
group by personId
having count(distinct entitlement_id) >= 5
) X
Your request isn't exactly clear. Are you asking for the count of people with more than five entitlement rows whether they exist on multiple serial numbers or not? If so, you could do something like:
Select Count(*) As CountOfPeople
From Person_tbl As P
Where Exists (
Select 1
From serialNumbers As S1
Join entitlement_tbl As E1
On E1.serialNumberId = S.serialNumberId
Where S1.personId = P.personId
Having Count(*) >= 5
)
Or is it that you are asking to find the number of people that have a serialNumber with more than five entitlements? If that is the case, then you could do something like:
Select Count(*) As CountOfPeople
From Person_tbl As P
Where Exists (
Select 1
From serialNumbers As S1
Join entitlement_tbl As E1
On E1.serialNumberId = S.serialNumberId
Where S1.personId = P.personId
Having Count( Distinct S1.serialNumberId ) >= 5
)

SQL query for finding row with same column values that was created most recently

If I have three columns in my MySQL table people, say id, name, created where name is a string and created is a timestamp.. what's the appropriate query for a scenario where I have 10 rows and each row has a record with a name. The names could have a unique id, but a similar name none the less. So you can have three Bob's, two Mary's, one Jack and 4 Phil's.
There is also a hobbies table with the columns id, hobby, person_id.
Basically I want a query that will do the following:
Return all of the people with zero hobbies, but only check by the latest distinct person created, if that makes sense. Meaning if there is a Bob person that was created yesterday, and one created today.. I only want to know if the Bob created today has zero hobbies. The one from yesterday is no longer relevant.
select pp.id
from people pp, (select name, max(created) from people group by name) p
where pp.name = p.name
and pp.created = p.created
and id not in ( select person_id from hobbies )
SELECT latest_person.* FROM (
SELECT p1.* FROM people p1
WHERE NOT EXISTS (
SELECT * FROM people p2
WHERE p1.name = p2.name AND p1.created < p2.created
)
) AS latest_person
LEFT OUTER JOIN hobbies h ON h.person_id = latest_person.id
WHERE h.id IS NULL;
Try This:
Select *
From people p
Where timeStamp =
(Select Max(timestamp)
From people
Where name = p.Name
And not exists
(Select * From hobbies
Where person_id = p.id))