I want to select DISTINCT(p.ptype) at the same time I also want to get the c.category if p.ptype is not in the set of c.ptype
Database Table: p
id ptype
1 Shirts
2 Cups
3 Shirts
4 Mugs
Database Table: c
id category ptype
1 Test Pants, Shirts, TShirts
2 Test1 Cups, Mats, Rugs
The SQL command I tried is as follows
SELECT DISTINCT(p.ptype), IF(FIND_IN_SET(p.ptype, c.ptype), c.category,'') as category
FROM p, c
This outputs p.ptype which are in set twice. One time with a blank c.category field and one with filled c.category.
However the desired output is as follows
ptype category
Shirts Test
Cups Test1
Mugs
Try doing an explicit LEFT JOIN on the ptype from the p table being present in the CSV list in the c table:
SELECT DISTINCT p.ptype, COALESCE(c.category, '') AS category
FROM p
LEFT JOIN c
ON FIND_IN_SET(p.ptype, c.ptype) > 0
In your original query, were doing a cross join. This generates all possible combinations between the records of the two tables. It would be difficult to arrive the correct answer using a cross join, so a left join is preferable.
Demo here:
SQLFiddle
Related
Background
Products can be sold as bundles. Following tables are present: products, bundles, bundles_products, orders, orders_products.
An order would be said to "contain" a bundle if it contains all the bundle's products.
Problem
How would one go about counting orders for bundles?
Example
products table
id name
1 broom
2 mug
3 spoon
4 candle
bundles table
id name
1 dining
2 witchcraft
bundles_products table
bundle_id product_id
1 2
1 3
2 1
2 4
orders_products table
order_id product_id
1000 1
1000 3
1001 1
1001 2
1001 3
The query would return the following table:
bundle orders
dining 1
witchcraft 0
Notes
The example intentionally misses the orders table as it is not relevant what it contains.
Of course, this could be approached imperatively, by writing some code and gathering the data, but I was hoping there is a declarative, SQL way of querying for this kind of things?
One idea I had was to use a GROUP_CONCAT to concatenate all the products of a bundle and somehow compare that with products of each order. Still, a long way from clear.
One way is to use two Derived Tables (subqueries). In first subquery, we will fetch the total number of unique products for every bundle. In the second subquery, we will fetch the total products in an order, for a combination of order and bundle.
We will LEFT JOIN them on bundle_id as well as matching the total count of products per bundle in them. Eventually, we will do a grouping on bundle, and count the number of orders matching successfully.
SELECT dt1.id AS bundle_id,
dt1.name AS bundle,
Count(dt2.order_id) AS orders
FROM (SELECT b.id,
b.name,
Count(DISTINCT bp.product_id) AS total_bundle_products
FROM bundles AS b
JOIN bundles_products AS bp
ON bp.bundle_id = b.id
GROUP BY b.id,
b.name) AS dt1
LEFT JOIN (SELECT op.order_id,
bp.bundle_id,
Count(DISTINCT op.product_id) AS order_bundle_products
FROM orders_products AS op
JOIN bundles_products AS bp
ON bp.product_id = op.product_id
GROUP BY bp.bundle_id,
op.order_id) AS dt2
ON dt2.bundle_id = dt1.id
AND dt2.order_bundle_products = dt1.total_bundle_products
GROUP BY dt1.id,
dt1.name
SQL Fiddle DEMO
Here's the brief example, which lacks some parts, I omitted because I don't know precise database structure. Logic is such:
Temp table is generated, which consists of 3 rows - order, count of
products related to bundle, count of products in bundle
Then we select only orders from this table in which we have those last two
variables equal
select count(order_id) from orders
left join(
select count(*) from bundles_products as bundle_amount,
sum(case when orders_products in (
select names from bundles_products where bundle_id='1') then 1 else 0) as order_total,
orders.order_id
left join product on bundle_products.product_id = products.product_id
left join orders on products.product_id = orders_products.product_id
where bundle_products.bundle_id ='1'
) as my_table
on orders.order_name = my_table.orders
where my_table.bundle_amount = my_table.order_total
Edit: I posted this as a response to previous version of the question, without detailed explanation.
Edit2: fixed query a bit. It can be starting point. Logic is still the same, you can get amount of orders for each bundle_id using it
I looked and tried various examples, but still not get the results I need.
With have a table called "item" which has metadata about media (books, vidoes, mp3 ,etc"
and a table "item_format" with the URL of the video
and Table with "categories"
and a relational to called "categories_item"
I want to find all the record on our youTube videos sorted by categories. so that if you look for "parenting" we get list a list a youTube and the title.
parenting https://youtube/v/K8cJKirMo-A Love is The Tey
https://youtube/v/uodqINpEC_w Discipline
https://youtube/v/Ko-ZboCzR64 Become Her Friend
and so on, listing all the categories assigned
item.media-type LIKE '%video%'
item
===============
item_id media_type title
1 video/teachings Discipline
2 video/news December Update
3 video/landscape Quad Copter Noni Field
item_format
=================
item_format_id item_id format
1 2 https://youtube/v/K8cJKirMo-A
2 4 https://youtube/v/uodqINpEC_w
category
=================
category_id item_id name
1 2 parenting
2 4 ethics
category_item # the bridging table between item/category
=================
category_item_id category_id item_id
1 2 2
2 4 3
I tried things like this but get errors, frankly out my depth here
select item.title, item_format.format
from item i
left join item_format if
on i.item_id = if.item_id
left join category_item ci
on i.item_id = ci.item_id
left join category c
on ci.category_id = category_id
where i.media_type LIKE '%video5'
order by c.name
I can't get to first base
Unknown column 'item.title' in 'field list' #line 1
your query is ok , but a small change need. you use table alias. in first line just use table alias instead of table name
select i.title, if.format,c.name
from item i
left join item_format if on (i.item_id = if.item_id)
left join category_item ci on (i.item_id = ci.item_id)
left join category c on (ci.category_id = c.category_id)
where i.media_type LIKE '%video5'
order by c.name
you have to provide alias once you have given it:
see if it works:
select i.title, if.format,c.name
from item i
inner join item_format if
on i.item_id = if.item_id
inner join category c
on i.item_id=c.item_id
inner join category_item ci
on c.category_id=ci.category_id
where i.media_type LIKE '%video%'
order by c.name
or this:
select i.title,if.format,c.name
from item i,item_format if,category c, category_item ci
where
i.item_id=if.item_id and
i.item_id=c.item_id and
c.category_id=ci.category_id
order by c.name;
I have a main table with two relations.
Data structure and example:
A/Employee
id fields
1 Mike Miller
2 Lisa Miller
B/Skill
aid name
1 SQL
1 PHP
C/Language
aid name
1 German
I need a query which shows results from the main table and searches for a keyword in the relation tables.
Search for Miller -> Mike Miller, Lisa Miller
Search for SQL -> Mike Miller
Search for German -> Mike Miller
There are 10.000 rows in the main table and 100.000 relations.
I tried it with JOIN but the query is really slow.
Also the same row from the main table is displayed a view several times when there are more than one relations for this row:
Search for Miller
Returns: Mike Miller, Mike Miller
(Mike Miller displayed more than one time)
SELECT fields
FROM A
JOIN B ON id = B.aid JOIN C ON id = C.aid
WHERE fields LIKE '%"+$search+"%' OR B.name LIKE '%"+$search+"%' OR C.name LIKE '%"+$search+"%'"
I tried to fix the second problem with DISTINCT but now rows without relations are not displayed.
I want to display every row from the main table exactly one time. Which query do I need?
The problem with your first query, as you mention yourself, is that you get several duplicate rows returned. Not strange, since I guess the relation between table A and table B & C is one-to-many.
In your next attempt you added DISTINCT, and that will indeed get rid off the duplicates, but the regular join (or inner join) will only return matches where data can be joined, i.e. where data exists in both joined tables.
Introducing LEFT JOIN:
SELECT DISTINCT fields
FROM A
LEFT JOIN B ON id = B.aid
LEFT JOIN C ON id = C.aid
WHERE fields LIKE '%"+$search+"%' OR B.name LIKE '%"+$search+"%' OR C.name LIKE '%"+$search+"%'"
This will always search all data from table A, and those from table B & C where joins can be made. The DISTINCT will make sure that only unique rows are returned. You could also use GROUP BY for the same result, but that's usually used for aggregate methods.
Use LEFT JOIN.
Example:
SELECT distinct e.empname FROM Employee e
LEFT JOIN skill s ON s.aid = e.id
LEFT JOIN lang l ON l.aid = e.id
WHERE e.empname LIKE '%Miller%' OR s.name LIKE '%Miller%' OR l.name LIKE '%Miller%'
Proof SQL Fiddle
I have a table that contains as comma separated values in three fields the primary keys of three different tables, for example:
Table A (Main table):
id title companyid countryid regulatorid
1 tit1 1,2 2,3 2,1
2 tit2 3,1 1,2 1,3
Table B (company) table c (Country) table d(regulator)
id title id title id title
1 comp1 1 country1 1 regul1
2 comp2 2 country2 2 regulator2
3 comp3 3 country3 3 regulator
I want to get a result like:
id title company country regulator
1 tit1 comp1,comp2 country2,counrtry3 regulator2,regul1
2 tit2 comp3,comp1 country1,country2 regul1,regulator3
I have made query like:
select id,title,group_concat(table B.title) AS company,group_concat(table c.title) AS
country,group_concat(table d.title) AS regulator from table A INNER JOIN table b on
find_in_set(table B.id,table A.companyid) > 0 INNER JOIN table c on find_in_set(table
c.id,table A.countryid) > 0 INNER JOIN table d on find_in_set(table d.id,table
A.regulatorid) > 0
What do I have to change to get the current result?
This is such a horrible data format that before answering, I should make you promise to change the format if you have control over it. This should really be three additional separate association/junction tables. If you are going to use a relational database, use it right.
select a.id, a.title, group_concat(distinct b.title), group_concat(distinct c.title),
group_concat(distinct d.title)
from a left outer join
b
on find_in_set(b.id, a.companyid) > 0 left outer join
c
on find_in_set(c.id, a.countryid) > 0 left outer join
d
on find_in_set(d.id, a.regulatorid) > 0
group by a.id, a.title;
But it would be much much much better to change the table layout.
Suppose I have a Product table, and a
id product
1 Apple
2 Bag
3 Cat
4 Ducati
and a Cart table
id user_id product_id
1 1 2
2 1 3
3 2 1
4 3 1
So, I want to look at a particular user and see what he/she does NOT have in their Cart.
In other words, in the above example
SELECT ...... WHERE user_id=1 .....
would return Apple and Ducati because User 1 already has Bag and Cat.
(This may well duplicate another question but there are so many variations I couldn't find the exact match and put in these simple terms may help)
Perform a left join from product to all products purchased by user1, which can be retrieved with a subselect in the join. This will cause all product id's that are not in user1's care to have null product ids. The where clause will select all null product id's meaning they will not have been in a users cart, essentially filtering purchased items.
select p.name
from product p
left join (select product_id, user_id
from cart where user_id = 1)
c
on p.id = c.product_id
where c.product_id is null;
SQL Fiddle: http://sqlfiddle.com/#!2/5318eb/17
Select
*
From Product p
Where p.id Not In
(
Select c.product_id
From Cart c
Where User ID = ____
)
SELECT product FROM product_table
WHERE product NOT IN
(SELECT product_id FROM cart_table WHERE user_id = 1);
This will give you all product for all users which are not in there cart.
select c.user_id,a.Product
from cart c Cross Join product a
left Join
cart b on b.product_id=a.id and c.user_id=b.user_Id
where b.product_id is null
group by c.user_id,a.Product
Sql Fiddle Demo