mysql inner join condition to ignore duplicate ids - mysql

Given below the list of tables.
Order the shop ids in the descending order based on the sum of number of clicks for a product in the locations [23,24,25,26,27].
Given product_id = 8
link_shops_locations
shop_id | location_id
---------------------
1 | 23
2 | 24
3 | 25
3 | 26
3 | 27
products_clicks
shop_id | product_id | clicks
-----------------------------
1 | 8 | 1
2 | 7 | 3
2 | 8 | 87
3 | 8 | 21
3 | 8 | 9
link_products_shops
product_id | shop_id
---------------------
7 | 1
8 | 1
8 | 1
8 | 2
8 | 2
8 | 1
7 | 3
8 | 3
This is what I have tried,
SELECT SUM(c.clicks) as no,
s.shop_id
FROM link_products_shops l
INNER JOIN products_clicks c
ON c.product_id = l.product_id
INNER JOIN link_shops_locations s
ON s.shop_id = c.shop_id // duplicate shop_ids gives wrong SUM
WHERE s.location_id IN (23,24,25,26,27)
GROUP BY s.shop_id
ORDER BY no DESC;
My problem is, since the link_shops_locations table has 3 shop_ids, the resulting expected SUM is multiplied by 3. How do I solve this? The INNER JOIN condition of link_shops_locations has something to do with this?. A little help will be very useful.

[1]You missed one more condition in INNER JOIN with products_clicks.
[2]Also you ignored product_id. Have a look at this query.
[3]You had inserted product_id=8 and shop_id=1 three times in link_products_shops table.
select shop_id, SUM(NoOfClicks) FROM(SELECT distinct c.shop_id, c.clicks AS NoOfClicks
FROM products_clicks c
INNER JOIN link_products_shops l
ON c.product_id = l.product_id
AND c.shop_id = l.shop_id
INNER JOIN link_shops_locations s
ON s.shop_id = c.shop_id and s.shop_id = l.shop_id
WHERE s.location_id IN (23,24,25,26,27)
AND c.product_id = 8) AS TabShopCounters
GROUP BY shop_id
ORDER BY NoOfClicks DESC;
SQL Fiddle - http://www.sqlfiddle.com/#!2/b22fe/8

Related

MySQL Summing & Counting Child Values in Another Table

BOOKINGS TABLE
id | price | anotherVal
-----------------------------
1 | 10000 | *
2 | 20000 | *
3 | 1000 | *
4 | 8000 | *
BOOKING PAYMENTS TABLE
id | bookingId | amount | currencyId | mxnAmount
--------------------------------------------------
1 | 1 | 100.00 | 1 | 100.00
2 | 1 | 300.00 | 3 | 6400.00
3 | 2 | 500.21 | 1 | 500.21
4 | 4 | 123.95 | 6 |
4 | 4 | 800.00 | 1 | 800.00
I need to get all BOOKINGS_TABLE columns and then for each booking add up the mxnAmount column, but also the result should tell if all rows in BOOKING_PAYMENTS_TABLE had an mxnAmount so i can know if the mxnAmount is final or there's some rows left to be updated, i have a query that works for the first part:
SELECT b.*, SUM(p.mxnAmount) FROM bookings b LEFT JOIN bookingPayments p ON b.id = p.bookingId GROUP BY b.id
I figured i could make us of COUNT() to count all rows in BOOKING_PAYMENTS_TABLE but then how can i get the number for the rows that have an mxnAMOUNT?
SELECT b.*, SUM(p.mxnAmount), COUNT(p.id) FROM bookings b LEFT JOIN bookingPayments p ON b.id = p.bookingId GROUP BY b.id
I tried this:
SELECT b.*, SUM(p.mxnAmount), COUNT(p.id), COUNT(pp.id) FROM bookings b LEFT JOIN bookingPayments p ON b.id = p.bookingId LEFT JOIN bookingPayments pp ON b.id = pp.bookingId WHERE pp.mxnAmount IS NOT NULL GROUP BY b.id
But then the query returns only bookings that have all their payments rows with an mxnAmount, any leads?
I figured i could make us of COUNT() to count all rows in BOOKING_PAYMENTS_TABLE but then how can i get the number for the rows that have an mxnAMOUNT?
Just COUNT() that particular column: this gives you the number of non-null values in the column for each group:
SELECT b.*, SUM(p.mxnAmount), COUNT(p.id), COUNT(p.mxnAmount)
FROM bookings b
LEFT JOIN bookingPayments p ON b.id = p.bookingId
GROUP BY b.id
If you want to know if any mxmamount in the group is missing, you can do:
MAX(p.id IS NOT NULL AND p.mxnAmount IS NULL) has_missing_mxnAmount

How i implement a query to display data by using 3 tables data which are depend on each other?

My Three table are like this
Requistion Item Sub_Category
id | Item_id | status Item_id | name | sub_cat sub_cat | Sub_name
1 | 10 | 100 10 | apple |1 1 | Fruits
2 | 20 | 100 20 | Beans |2 2 | Vegitable
3 | 30 | 100 30 | banana |1 3 | Drinks
4 | 40 | 200 40 | Water |3
I want to Display all Sub_name With below condition
Requistion Table status=100
Result Without Duplicate names
As per the above tables Result must be like
Sub_name
Fruits
Vegitable
Using DISTINCT Sub_name with JOINs this can be done:
SELECT DISTINCT S.Sub_name
FROM Requistion R
JOIN Item I
ON I.Item_id = R.Item_id
JOIN Sub_Category S
ON S.sub_cat = I.sub_cat
WHERE R.status = 100
SELECT * FROM Requistion INNER JOIN Item on Item.Item_id=Requistion.Item_id INNER JOIN Sub_Category on Sub_Category.sub_cat=Item.sub_cat where STATUS=100
You can use the below query to get this using group by to get unique sub_name,
select Sub_name from Sub_Category sc
JOIN Item i on i.sub_cat = sc.sub_cat
JOIN Requistion r on r.Item_id = i.Item_id
WHERE r.status = 100
GROUP BY sc.Sub_name
You can use distinct for the same,
select DISTINCT Sub_name from Sub_Category sc
JOIN Item i on i.sub_cat = sc.sub_cat
JOIN Requistion r on r.Item_id = i.Item_id
WHERE r.status = 100

Filter query with order by and limit when using left join

How to filter query with order by and limit when using left join
store_profile
id + store_name
1 | Accessorize.me
2 | Active IT
3 | Edushop
4 | Gift2Kids
5 | Heavyarm
6 | Bamboo
store_fee
id + store_id + date_end
1 | 1 | 27-6-2013
2 | 2 | 29-8-2013
3 | 3 | 02-6-2013
4 | 4 | 20-4-2013
5 | 4 | 01-7-2013
6 | 4 | 28-9-2013
7 | 5 | 03-9-2013
8 | 6 | 01-9-2013
my previous query
$order_by_for_sort_column = "order by $column" //sorting column
$query = "SELECT * FROM store_profile sp LEFT JOIN store_fee sf ON (sf.store_id = sp.id) $order_by_for_sort_column";
what i want is order by id desc and limit 1 for table store_fee not for for entire query. So i can grab the latest date in date_end for each store.
As you can see for store_id 4(store_fee) i have 3 different date and i just want grab the latest date.
and the result should be something like this
1 | Accessorize.me 27-6-2013
2 | Active IT 29-8-2013
3 | Edushop 02-6-2013
4 | Gift2Kids 28-9-2013
5 | Heavyarm 03-9-2013
6 | Bamboo 01-9-2013
SELECT a.id, a.store_name, MAX(b.date_End) date_end
FROM store_profile a
LEFT JOIN store_fee b
ON a.ID = b.store_ID
GROUP BY a.id, a.store_name
SQLFiddle Demo
but if the datatype date_End column is varchar, the above query won't work because it sorts the value by character and that it can mistakenly gives undesired result. 18-1-2013 is greater than 01-6-2013.
To further gain more knowledge about joins, kindly visit the link below:
Visual Representation of SQL Joins
SELECT *
FROM store_profile AS sp
LEFT JOIN (
SELECT store_id, MAX(date_end)
FROM store_fee
GROUP BY store_id
) AS sf
ON sp.id=sf.store_id;

SELECT DISTINCT return duplicated values

I have 3 tables players positions players_national
I need to SELECT DISTINCT positions for players_national where player_positon is associated with players table.
My Tables are like this:
Players Table
-----------------------------------------------------------------------
| player_id player_name player_team player_position |
-----------------------------------------------------------------------
1 KAKA 12 1
2 Ronaldo 7 2
3 Adriano 10 2
Positions Table
-------------------------------------------------------
| position_id position_name |
-------------------------------------------------------
1 Midfield
2 Forward
Players_national Table
-------------------------------------------------------------------
| player_id player_team player_national_team |
-------------------------------------------------------------------
1 12 4
2 7 4
3 19 4
My Dream output is this
---------------------------------------------------
| player_national_team position |
---------------------------------------------------
4 1
4 2
My Query is like this:
SELECT DISTINCT players. * , positions. * , players_national. *
FROM players
LEFT JOIN positions ON positions.position_id = players.player_position
LEFT JOIN players_national ON players_national.player_id = players.player_id
WHERE players_national.player_id = players.player_id AND players_national.player_national_team = 4
If you select everything from every table then every row will be DISTINCT; you need to limit your SELECT clause to just the fields you want to be DISTINCT
SELECT DISTINCT positions.position_id, players_national.player_national_team
FROM players
LEFT JOIN positions ON positions.position_id = players.player_position
LEFT JOIN players_national ON players_national.player_id = players.player_id
WHERE players_national.player_id = players.player_id AND players_national.player_national_team = 4
Distinct player_position on table players, where is join to table players_national
SELECT
DISTINCT(players.player_position),
players_national.player_national_team
FROM players INNER JOIN players_national;
result:
+-----------------+----------------------+
| player_position | player_national_team |
+-----------------+----------------------+
| 1 | 4 |
| 2 | 4 |
+-----------------+----------------------+
2 rows in set

Finding the MIN value that appears for each unique value in either of two other columns

Given the following (simplified) tables:
People p
id name registered
-----------------------------------
1 Geoff 2011-03-29 12:09:08
2 Phil 2011-04-29 09:03:54
3 Tony 2011-05-29 21:22:23
4 Gary 2011-06-21 22:56:08
...
Items i
date p1id p2id
----------------------------------------
2011-06-29 20:09:44 1 2
2011-06-26 10:45:00 1 3
2011-06-23 12:22:43 2 3
2011-06-22 13:07:12 2 4
...
I'd like:
The earliest single i.date that each p.id appears in either column p1id or p2id; or p.registered if they feature in neither.
So far, I've tried:
CREATE TEMPORARY TABLE temp (id INT);
INSERT INTO temp (id)
SELECT DISTINCT u FROM (
SELECT p1id AS u FROM Items UNION ALL
SELECT p2id AS u FROM Items
)tt;
SELECT registered,id FROM People
WHERE id NOT IN (SELECT id FROM temp);
Which gets me as far as the second part, albeit in a fairly clumsy way; and I'm stuck on the first part beyond some sort of external, scripted iteration through all the values of p.id (ugh).
Can anyone help?
I'm on MySQL 5.1 and there's ~20k people and ~100k items.
One more solution:
SELECT id, name, IF(min_date1 IS NULL AND min_date2 IS NULL, registered, LEAST(COALESCE(min_date1, min_date2), COALESCE(min_date2, min_date1))) date FROM (
SELECT p.id, p.name, p.registered, MIN(i1.date) min_date1, MIN(i2.date) min_date2 FROM people p
LEFT JOIN items i1
ON p.id = i1.p1id
LEFT JOIN items i2
ON p.id = i2.p2id
GROUP BY id
) t;
OR this:
SELECT p.id, p.name, COALESCE(MIN(i.date), p.registered) FROM people p
LEFT JOIN (
SELECT p1id id, date FROM items
UNION ALL
SELECT p2id id, date FROM items
) i
ON p.id = i.id
GROUP BY id;
Result:
+------+-------+---------------------+
| id | name | date |
+------+-------+---------------------+
| 1 | Geoff | 2011-06-26 10:45:00 |
| 2 | Phil | 2011-06-22 13:07:12 |
| 3 | Tony | 2011-06-23 12:22:43 |
| 4 | Gary | 2011-06-22 13:07:12 |
+------+-------+---------------------+
This is tested in Postgres, but I think it ought to work in MySQL with few or no changes:
SELECT p.id,COALESCE(MIN(x.date),p.registered) AS date
FROM p
JOIN (
SELECT p.id,MIN(i.date) AS date
FROM p
JOIN i ON (p.id=i.p1id)
GROUP BY p.id
UNION
SELECT p.id,MIN(i.date) AS date
FROM p
JOIN i ON (p.id=i.p2id)
GROUP BY p.id
) AS x ON x.id = p.id
GROUP BY p.id,p.registered;
Output (given your sample data):
id | date
----+---------------------
3 | 2011-06-23 12:22:43
1 | 2011-06-26 10:45:00
2 | 2011-06-22 13:07:12
4 | 2011-06-22 13:07:12
(4 rows)