I have a mysql Table T1 consisting of two columns of INTs that links a car_id to a part_id. A single car_id can have multiple part_ids, and the same part_id can correspond to more than one car_id. For example,
car_id part_id
1 1
1 2
1 8
2 3
3 4
4 2
4 6
...
10 1
10 2
...
20 1
20 2
20 8
To get all the part_ids associated with car_id = 1, I run the query,
SELECT car_id, part_id FROM T1 WHERE car_id=1
and get the result:
car_id part_id
1 1
1 2
1 8
Now, I want to find all the remaining car_ids that contain at least (say >= 2/3) of the part_ids associated with car_id=1. (In this example, I should get all car_ids that have at least 2 of the part_ids 1,2, and 8 as shown after my SELECT query. So, I should get car_ids 1,10, and 20).
I can find the car_ids that contain All of the part_ids 1,2, and 8 using:
SELECT car_id, part_id
FROM T1
WHERE part_id = ALL (SELECT part_id FROM T1 WHERE car_id=1). The result is car_ids 1 and 20.
I can find the car_ids that contain ANY of the values 1,2, and 8 using:
SELECT car_id, part_id
FROM T1
WHERE part_id = ANY (SELECT part_id FROM T1 WHERE car_id=1). The result is car_ids 1,4,10 and 20.
How can I specify some number between ANY and ALL?
To get all car_ids that have 2 or more of car 1's part_ids do
SELECT car_id,
group_concat(part_id) as part_ids
FROM T1
WHERE part_id in (SELECT part_id FROM T1 WHERE car_id = 1)
group by car_id
having count(distinct part_id) >= 2
Here is one way:
select car_id
from (select cp.car_id,
sum(case when cp.part_id is not null and cp1.part_id is not null then 1 else 0 end) as PartsInCommon,
sum(case when cp.part_id is not null and cp1.part_id is null then 1 else 0 end) as ThisCarOnly,
sum(case when cp.part_id is null and cp1.part_id is not null then 1 else 0 end) as ThatCarOnly
from CarParts cp full outer join
(select part_id
from CarParts cp
where car_id = 1
) cp1
on cp.part_id = cp1.part_id
group by cp.car_id
) t
where PartsInCommon / (PartsInCommon + ThisCarOnly + ThatCarOnly) >= 2.0/3
This query counts the number of parts common to both cars or in one or the other. The where clause then defines the particular condition.
If you want the list of parts, then Juergen has the right idea with the group_concat(), although you don't specify this in the question.
Try this query. I have tried as much as i can
SELECT
car_id,
GROUP_CONCAT(part_id)
FROM cars
WHERE FIND_IN_SET
(part_id ,(SELECT GROUP_CONCAT(part_id) FROM cars WHERE car_id = 1))
GROUP BY car_id
HAVING COUNT(part_id) >= 2
Here is the sqlfiddle Demo http://sqlfiddle.com/#!2/8e563/17
Related
I have a table of products sales
id product_id price_sold
1 1 500
2 1 300
3 2 100
4 3 200
5 3 100
I want to be able to sum the prices by different subsets of products, say: sum of prices per the group of proucts 1,2. and another calculation of sum of prices per the group of products 2,3, so the needed result will be:
group 1, 900
group 2, 400
Can you help with efficient and elegant way to do that?
Doing what you want is a bit challenging, because the groups overlap. You have two options. The first is to do conditional aggregation and put the results in columns:
select sum(case when product_id in (1, 2) then price_sold end) as group1,
sum(case when product_id in (2, 3) then price_sold end) as group2
from productsales ps;
To get the results on separate rows, you could then pivot this result. Or, you could do the calculation directly. For this to work, you need to construct a table describing the groups:
select pg.grpid, sum(ps.price_sold)
from productsales ps
join
(
select 1 as grpid, 1 as product_id
union all
select 1 as grpid, 2 as product_id
union all
select 2 as grpid, 2 as product_id
union all
select 2 as grpid, 3 as product_id
) pg on ps.product_id = pg.product_id
group by pg.grpid;
SQL Fiddle:
SELECT 'GROUP 1' AS `Group`, SUM(price_sold) AS PriceSum
FROM MyTable
WHERE product_id = 1 OR product_id = 2
UNION
SELECT 'GROUP 2', SUM(price_sold)
FROM MyTable
WHERE product_id = 2 OR product_id = 3
The result looks like:
I have a table like this:
**lead_id** **form_id** **field_number** **value**
1 2 1 Richard
1 2 2 Garriot
2 2 1 Hellen
2 2 2 Garriot
3 2 1 Richard
3 2 2 Douglas
4 2 1 Tomas
4 2 2 Anderson
Where field_number = 1 is the name and field_number = 2 is the surname.
I would like to find entries that are equal by name OR surname and group them by lead_id, so the output could be like this:
1
2
3
Any thoughts on how this can be done?
This should work and be reasonably efficient (depending upon indexes):
select distinct lead_id
from tablename as t1
where exists (
select 1
from tablename as t2
where t1.field_number = t2.field_number
and t1.value = t2.value
and t1.lead_id <> t2.lead_id
)
Select leadid from (
Select DISTINCT leadid,value from tablename
Where fieldnumber=1
Group by leadid,value
Having count(value) >1
Union all
Select DISTINCT leadid,value from tablename
Where fieldnumber=2
Group by leadid,value
Having count(value) >1
) as temp
Surely there is a faster option
I've got the following associative table between packages and products (simplified):
package_id product_id count
1 1 6
1 2 1
1 3 1
2 1 6
2 2 1
3 1 6
4 1 8
4 2 1
I'm trying to work out how to create an query which is able to select specific package_id's which contain exactly the products and their counts I supply. So if I'd be trying to find the package that contains: (product_id = 1 AND count = 6) AND (product_id = 2 AND count = 1), it should only return package_id 2 and not the others, because those contain other products and / or other counts.
I'd be happy to work this out in my code (PHP) instead of SQL, but since I'm trying to get to the bottom of queries, I'd like to know how this is done.
This is called Relational Division
SELECT a.package_ID
FROM tableName a
WHERE (a.product_ID = 1 AND a.count = 6) OR
(a.product_ID = 2 AND a.count = 1)
GROUP BY a.package_ID
HAVING COUNT(*) = 2 AND
COUNT(*) = (SELECT COUNT(*) FROM tableName WHERE package_ID = a.package_ID)
SQLFiddle Demo
OR
SELECT package_ID
FROM tableName
WHERE (product_ID, `count`) in ((1, 6), (2, 1))
GROUP BY package_ID
HAVING COUNT(DISTINCT product_ID, `count`) = 2 AND
COUNT(*) = (SELECT COUNT(*) FROM tableName WHERE package_ID = a.package_ID)
SQLFiddle Demo
I am writing a query to grab the items that a specific user_id was the first to use. Here is some sample data -
item_id used_user_id date_used
1 1 2012-08-25
1 2 2012-08-26
1 3 2012-08-27
2 2 2012-08-27
3 1 2012-08-27
4 1 2012-08-21
4 3 2012-08-24
5 3 2012-08-23
query
select item_id as inner_item_id, ( select used_user_id
from test
where test.item_id = inner_item_id
order by date_used asc
limit 1 ) as first_to_use_it
from test
where used_user_id = 1
group by item_id
It returns the correct values
inner_item_id first_to_use_it
1 1
3 1
4 1
but the query is VERY slow on a giant table. Is there a certain index that I can use or a better query that I can write?
i can't get exactly what you mean because in your inner query you have sorted it by their used_user_id and and on your outer query you have filtered it also by their userid. Why not do this directly?
SELECT DISTINCT item_id AS inner_item_id,
used_user_id AS first_to_use_it
FROM test
WHERE used_user_id = 1
UPDATE 1
SELECT b.item_id,
b.used_user_id AS first_to_use_it
FROM
(
SELECT item_ID, MIN(date_used) minDate
FROM tableName
GROUP BY item_ID
) a
INNER JOIN tableName b
ON a.item_ID = b.item_ID AND
a.minDate = b.date_used
WHERE b.used_user_id = 1
I have 3 tables: items, purchases, and collaborators. A user can own an item, purchase an item, or be a collaborator on an item. Additionally, items that are purchased can be rated up, +1, or down, -1. An owner or collaborator can't purchase their own item.
I'd like to get all items for a given user and also display the ratings on each item.
Here's my tables:
items | purchases | collaborators
i_id item_id user_id | p_id item_id user_id rating |c_id item_id user_id
1 1 11 | 1 1 13 -1 | 1 1 12
2 2 12 | 2 2 11 1 | 2 2 13
3 3 13 | 3 3 12 NULL |
| 4 1 14 -1 |
Here's my MYSQL query so far:
select *, count(p_id) as tots, sum(rating=1) as yes, sum(rating= '-1') as no
from items
left join purchases
on items.item_id=purchases.item_id
left join collaborators
on items.item_id=collaborators.item_id
where items.user_id=13 or purchases.user_id=13 or collaborators.user_id=13
group by items.item_id
Here's my expected results for user_id=11 (changing each user_id in the WHERE clause):
item_id tots yes no
1 2 0 2
2 1 1 0
// notice how user_id=11 doesn't have anything to do with item_id=3
Here's my expected results for user_id=12:
item_id tots yes no
1 2 0 2
2 1 1 0
3 1 1 0
Here's my expected results for user_id=13:
item_id tots yes no
1 2 0 2
2 1 1 0
3 1 1 0
//notice user_id=13 should have same results as user_id=12. Although, their
relation to each of the 3 items is different, they still either purchased,
own, or collaboratored on each of them.
Unfortunately, I get the first two results but not the correct one for user_id=13.
For user_id=13, item_id=1 the tots=1 and not tots=2 for some reason I can't understand.
Any thoughts, such as, "its better to separate this into 2 queries", would be greatly appreciated,
I'm still not entirly sure I understand you correct but you could try following statement and let us work from there.
Edit
Following statement returns the expected results.
You can verify this (using SQL Server) here.
The gist of this is to
select all possible user_id and item_id combinations from your three tables
select the counts/ratings for each item
combine the results
SQL Statement
SELECT u.user_id, pt.item_id, pt.cnt, pt.yes, pt.no
FROM (
SELECT user_id, item_id, title FROM items
UNION SELECT user_id, item_id, NULL FROM purchases
UNION SELECT user_id, item_id, NULL FROM collaborators
) u INNER JOIN (
SELECT COUNT(*) AS cnt
, SUM(CASE WHEN ISNULL(rating, 1) = 1 THEN 1 ELSE 0 END) AS yes
, SUM(CASE WHEN rating =-1 THEN 1 ELSE 0 END) AS no
, item_id
FROM purchases
GROUP BY
item_id
) pt ON pt.item_id = u.item_id
MYSQL statement
SELECT u.user_id, pt.item_id, pt.cnt, pt.yes, pt.no, u.title
FROM (
SELECT user_id, item_id, title FROM items where user_id=13
UNION SELECT user_id, item_id, NULL FROM purchases where user_id=13
UNION SELECT user_id, item_id, NULL FROM collaborators where user_id=13
) u INNER JOIN (
SELECT COUNT(*) AS cnt
, SUM(rating=1) AS yes
, SUM(rating =-1) AS no
, item_id
FROM purchases
GROUP BY
item_id
) pt ON pt.item_id = u.item_id