Getting data from 3 tables in MySQL query - mysql

I have 3 tables with structures like this:
store_locations
id store_id zip_code city state last_updated
--------------------------------------------------------------------------------
1 7438 37493 Seattle WA [timestamp]
1 7587 89574 Spokane WA [timestamp]
store_vehicles
id store_id vin_number
--------------------------------------------------------------------------------
1 7438 [some vin number]
1 7587 [some vin number]
store_sold_vehicles
id vin_number
--------------------------------------------------------------------------------
1 [some vin number]
1 [some vin number]
I am trying to output a table with store details including total_sales for that location. Here is the query I'm attempting, but it is not working as I'm missing a GROUP BY statement but don't know how to add it.
SELECT
COUNT(sales.id) AS total_sales,
locations.store_id AS store_id,
locations.zip_code AS zip_code,
locations.city AS city,
locations.state AS state
FROM store_locations locations
INNER JOIN store_vehicles vehicles ON vehicles.store_id = locations.store_id
INNER JOIN store_sold_vehicles sales ON sales.Vin = vehicles.Vin
GROUP BY vehicles.Vin
Is this possible to achieve without multiple queries?
Edit: Expected output would be something like this:
[
'store_id'=>8239,
'zip_code'=>27103,
'city'=>'San Francisco',
'state'=>'CA',
'last_updated'=>[timestamp],
'total_sales'=>121
]

You need to group by store and all non grouped elements:
SELECT
COUNT(sales.id) AS total_sales,
locations.store_id AS store_id,
locations.zip_code AS zip_code,
locations.city AS city,
locations.state AS state
FROM store_locations locations
INNER JOIN store_vehicles vehicles ON vehicles.store_id = locations.store_id
INNER JOIN store_sold_vehicles sales ON sales.Vin = vehicles.Vin
GROUP BY locations.store_id, locations.zip_code, locations.city, locations.state

I think this what you want
SELECT store_locations.store_id , zip_code , city , state , last_updated , total_sales
FROM store_locations JOIN (
SELECT store_id , COUNT(c.id) AS total_sales
FROM store_locations a JOIN store_vehicles b ON a.store_id = b.store_id JOIN store_sold_vehicles c ON b.vin_number = c.vin_number
WHERE store_id = 8239
GROUP BY store_id
) totalsales ON store_locations.store_id = totalsales.store_id

Related

To select a list of record satisfying multiple AND condition

I have two tables with the fields as below
______________
|Game_Details:|
|_____________|______
|Game_Id | Game_Name|
__________
|WC_Info:|
|________|_____________________________
|S/N | Game_Id | Country_Name| WC_Won|
I am trying to fetch the list of game_name which are being played in atleast the 4 countries (India, England,France, Italy) and the field WC_Won is 0 for all of them.
The below query is fetching incorrect details.can someone please suggest the way to get it.
Select G.Game_Name
From Game_Details G
Inner Join WC_Info W
On G.Game_Id=W.Game_Id
Where W.WC_Won=0
Group By Game_Name
Having Count(Country_Name in (India, England,France, Italy))>3
You may use this select statement :
Select distinct G.Game_Name
From Game_Details G
Where G.Game_Id in ( Select Game_Id
From WC_Info W
Where W.WC_Won=0
And Country_Name in ('India', 'England','France', 'Italy')
Group By Game_Id
Having Count( distinct Country_Name ) = 4 );
You are close:
Select G.Game_Name
From Game_Details G Inner Join
WC_Info W
On G.Game_Id = W.Game_Id
Where W.WC_Won = 0
Group By Game_Name
Having Count(distinct case when Country_Name in ('India', 'England', 'France', 'Italy')
then country_name
end) = 4;
I find the use of an inequality to be misleading. You want all four countries, so = 4 makes more sense than > 3 or >= 4.
Alternatively, you can filter on the country before the aggregation:
Select G.Game_Name
From Game_Details G Inner Join
WC_Info W
On G.Game_Id = W.Game_Id
Where W.WC_Won = 0 and
Country_Name in ('India', 'England', 'France', 'Italy')
Group By Game_Name
Having Count(distinct country_name) = 4;

same colum name with different table id in mysql

tables
person_id (primary key)
phs_people (person_id,first_name,last_name)
phs_cutomers (person_id,company_name)
phs_waiters (person_id,commission)
person_id is key between them.
So my question how can retrive customers firstname and last name, waiter firstname and lastname via person_id?
SELECT
c.first_name AS customer_Fist_name,
c.last_name AS Customer_LastName,
c.first_name AS WaiterFirstName,
c.last_name AS Waiter_LastName,
invoice_number, amount_tendered, sale_time, DATE_FORMAT( sale_time, '%d-%m-%Y' ) AS sale_date, phs_sales.sale_id AS sale_id, SUM( item_unit_price * quantity_purchased * ( 1 - discount_percent /100 ) ) AS amount_due
FROM (
phs_sales
)
LEFT JOIN phs_people c ON c.person_id = phs_sales.customer_id
AND person_id = phs_sales.waiter_id
JOIN phs_sales_items ON phs_sales_items.sale_id = phs_sales.sale_id
LEFT JOIN (
SELECT sale_id, SUM( payment_amount ) AS amount_tendered
FROM phs_sales_payments
WHERE payment_type <> 'Check'
GROUP BY sale_id
) AS payments ON payments.sale_id = phs_sales.sale_id
GROUP BY sale_id
ORDER BY sale_time DESC
LIMIT 25
if I execute this query, I get the following error:
customer_Fist_name NULL,Customer_LastName NULL, WaiterFirstName NULL, Waiter_LastName NULL,
You want to do JOIN's two times on the same table but with different values (customer's data and waiter's data), but you just use a JOIN once and give both conditions there.
To fix this, your have to JOIN the phs_people-Table twice like this:
...
LEFT JOIN phs_people AS c1 ON c1.person_id = phs_sales.customer_id
LEFT JOIN phs_people AS c2 ON c2.person_id = phs_sales.waiter_id
...
and then select the correct data like this:
SELECT
c1.first_name AS customer_Fist_name,
c1.last_name AS Customer_LastName,
c2.first_name AS WaiterFirstName,
c2.last_name AS Waiter_LastName,
...
PS: With this query, you should still get multiple NULL-Values, that's because half of your phs_sales-Table is filled with empty fields...

How to group by columns, and pick arbitrary non null other columns to display

I have a staging table with new address records and need to copy new cities into a mart table. I want to only have one entry in the mart for each city, state, zip combination, and I want to include latitude and longitude for the city. The address table has lat & long, but they could be anywhere within the city, or could be null.
The query I've got so far gets me the right data, but it's pulling one pair of lat & long arbitrarily. I'd prefer to pull from the ones that are not null.
SELECT a.city
,a.STATE
,a.country
,a.latitude
,a.longitude
FROM (
SELECT city
,STATE
,country
FROM staging2.address_daily s
WHERE NOT EXISTS (
SELECT *
FROM mart.city m
WHERE m.city_name = s.city
AND m.state_code = s.STATE
AND m.country_code = s.country
)
GROUP BY city
,STATE
,country
) sq --This subquery groups by city state and country
JOIN staging2.address_daily a
ON a.ID = (
SELECT ID
FROM staging2.address_daily i
WHERE i.city = sq.city
AND i.STATE = sq.STATE
AND i.country = sq.country LIMIT 1
) --This subquery takes the group, and picks one ID.
--The overall query is still flawed, as we're picking at random, and we should ideally pick a non-null latitude and longitude if they exist.
I'm using MySQL but would prefer to avoid things that are unique to MySQL.
From a SQL perspective this logic would work. For MySQL you might need to do some tweaks to the syntax -
SELECT sq.city
,sq.STATE
,sq.country
,a.latitude
,a.longitude
FROM (
SELECT city
,STATE
,country
FROM staging2.address_daily s
WHERE NOT EXISTS (
SELECT *
FROM mart.city m
WHERE m.city_name = s.city
AND m.state_code = s.STATE
AND m.country_code = s.country
)
GROUP BY city
,STATE
,country
) sq --This subquery groups by city state and country
LEFT JOIN staging2.address_daily a
ON a.ID = (
SELECT ID
FROM staging2.address_daily i
WHERE i.city = sq.city
AND i.STATE = sq.STATE
AND i.country = sq.country
AND NOT latitude IS NULL LIMIT 1
)
can you try this to see if it works for you
SELECT *
FROM (SELECT city,
STATE,
country,
A.latitude,
A.longitude,
ROW_NUMBER ()
OVER (PARTITION BY city, state, country
ORDER BY longitude, latitude DESC)
AS ROWNUM
FROM staging2.address_daily s
WHERE NOT EXISTS
(SELECT *
FROM mart.city M
WHERE M.city_name = s.city
AND M.state_code = s.STATE
AND M.country_code = s.country))
WHERE ROWNUM = 1

MySQL Filter result again

The goal here is to:
1. Fetch the row with the most recent date from EACH store for EACH ingredient.
2. From this result, compare the prices to find the cheapest store for EACH ingredient.
I can accomplish either the first or second goal in separate queries, but not in the same.
How can i filter out a selection and then apply another filter on the previous result?
EDIT:
I've been having problems with results that i get from MAX and MIN since it just fetches the rest of the data arbitrarily. To avoid this im supposed to join tables on multiple columns (i guess). Im not sure how this will work with duplicate dates etc.
I've included an image of a query and its output data.
If we use ingredient1 as an example, it exists in three separate stores (in one store twice on different dates).
In this case the cheapest current price for ingredient1 would be store3. If the fourth row dated 2013-05-25 was even cheaper, it would still not "win" due to it being out of date.
(Disregard brandname, they dont really matter in this problem.)
Would appreciate any help/input you can offer!
This question is really interesting!
So, first, we get the row with the most recent date from EACH store for EACH ingredient. (It is possible that the most recent dates from each store can be different.)
Then, we compare the prices from each store (regardless of the date) to find the least price for each ingredient.
The query below uses the GROUP_CONCAT function in good measure. Here's a SO question regarding the use of the function.
SELECT
i.name as ingredient_name
, MIN(store_price.price) as price
, SUBSTRING_INDEX(
GROUP_CONCAT(store_price.date ORDER BY store_price.price),
',',
1
) as date
, SUBSTRING_INDEX(
GROUP_CONCAT(s.name ORDER BY store_price.price),
',',
1
) as store_name
, SUBSTRING_INDEX(
GROUP_CONCAT(b.name ORDER BY store_price.price),
',',
1
) as brand_name
FROM
ingredient i
JOIN
(SELECT
ip.ingredient_id as ingredient_id
, stip.store_id as store_id
, btip.brand_id as brand_id
, CONVERT(SUBSTRING_INDEX(
GROUP_CONCAT(ip.ingredient_price_id ORDER BY ip.date DESC),
',',
1
), UNSIGNED INTEGER) as ingredient_price_id
, MAX(ip.date) as date
, CONVERT(SUBSTRING_INDEX(
GROUP_CONCAT(ip.price ORDER BY ip.date DESC),
',',
1
), DECIMAL(5,2)) as price
FROM ingredient_price ip
JOIN store_to_ingredient_price stip ON ip.ingredient_price_id = stip.ingredient_price_id
JOIN brand_to_ingredient_price btip ON ip.ingredient_price_id = btip.ingredient_price_id
GROUP BY
ip.ingredient_id
, stip.store_id) store_price
ON i.ingredient_id = store_price.ingredient_id
JOIN store s ON s.store_id = store_price.store_id
JOIN brand b ON b.brand_id = store_price.brand_id
GROUP BY
store_price.ingredient_id;
You can check the implementation on this SQL Fiddle.
The version below, which ignores the brand, is slightly smaller:
SELECT
i.name as ingredient_name
, MIN(store_price.price) as price
, SUBSTRING_INDEX(
GROUP_CONCAT(store_price.date ORDER BY store_price.price),
',',
1
) as date
, SUBSTRING_INDEX(
GROUP_CONCAT(s.name ORDER BY store_price.price),
',',
1
) as store_name
FROM
ingredient i
JOIN
(SELECT
ip.ingredient_id as ingredient_id
, stip.store_id as store_id
, CONVERT(SUBSTRING_INDEX(
GROUP_CONCAT(ip.ingredient_price_id ORDER BY ip.date DESC),
',',
1
), UNSIGNED INTEGER) as ingredient_price_id
, MAX(ip.date) as date
, CONVERT(SUBSTRING_INDEX(
GROUP_CONCAT(ip.price ORDER BY ip.date DESC),
',',
1
), DECIMAL(5,2)) as price
FROM ingredient_price ip
JOIN store_to_ingredient_price stip ON ip.ingredient_price_id = stip.ingredient_price_id
GROUP BY
ip.ingredient_id
, stip.store_id) store_price
ON i.ingredient_id = store_price.ingredient_id
JOIN store s ON s.store_id = store_price.store_id
GROUP BY
store_price.ingredient_id;
References:
Simulating First/Last aggregate functions in MySQL
This probably needs a couple of sub queries joined together.
This isn't tested (as I don't have your table definitions, nor any test data), but something like this:-
SELECT i.name AS ingredient,
ip.price,
ip.date,
s.name AS storename,
b.name AS brandname
FROM ingredient i
INNER JOIN ingredient_price ip
ON ingredient.ingredient_id = ingredient_price.ingredient_id
INNER JOIN store_to_ingredient_price stip
ON ingredient_price.ingredient_price_id = store_to_ingredient_price.ingredient_price_id
INNER JOIN store s
ON store_to_ingredient_price.store_id = store.store_id
INNER JOIN brand_to_ingredient_price btip
ON ingredient_price.ingredient_price_id = brand_to_ingredient_price.ingredient_price_id
INNER JOIN brand b
ON brand_to_ingredient_price.brand_id = brand.brand_id
INNER JOIN
(
SELECT i.ingredient_id,
stip.store_id,
ip.date,
MIN(ip.price) AS lowest_price
FROM ingredient i
INNER JOIN ingredient_price ip
ON ingredient.ingredient_id = ingredient_price.ingredient_id
INNER JOIN store_to_ingredient_price stip
ON ingredient_price.ingredient_price_id = store_to_ingredient_price.ingredient_price_id
INNER JOIN
(
SELECT i.ingredient_id,
stip.store_id,
MAX(ip.date) AS latest_date
FROM ingredient i
INNER JOIN ingredient_price ip
ON ingredient.ingredient_id = ingredient_price.ingredient_id
INNER JOIN store_to_ingredient_price stip
ON ingredient_price.ingredient_price_id = store_to_ingredient_price.ingredient_price_id
GROUP BY ingredient_id, store_id
) Sub1
ON i.ingredient_id = Sub1.ingredient_id
AND stip.store_id = Sub1.store_id
AND ip.date = Sub1.latest_date
GROUP BY i.ingredient_id, stip.store_id, ip.date
) Sub2
ON i.ingredient_id = Sub2.ingredient_id
AND stip.store_id = Sub2.store_id
AND ip.date = Sub2.date
AND ip.price = Sub2.lowest_price
Try this:
SELECT `newest`.ingredient, `newest`.store,
`newest`.brand, `newest`.price, `newest`.`latest_date`
FROM
(SELECT ingredient.name AS ingredient, store.name AS store,
brand.name AS brand, ingredient_price.price,
MAX( ingredient_price.date ) AS `latest_date`
FROM ingredient
LEFT OUTER JOIN ingredient_price
ON ingredient.ingredient_id = ingredient_price.ingredient_id
LEFT OUTER JOIN store_to_ingredient_price
ON ingredient_price.ingredient_price_id = store_to_ingredient_price.ingredient_price_id
LEFT OUTER JOIN store
ON store_to_ingredient_price.store_id = store.store_id
LEFT OUTER JOIN brand_to_ingredient_price
ON ingredient_price.ingredient_price_id = brand_to_ingredient_price.ingredient_price_id
LEFT OUTER JOIN brand
ON brand_to_ingredient_price.brand_id = brand.brand_id
GROUP BY ingredient.name) `newest`
ORDER BY `newest`.price
LIMIT 1

Counting refferers new members between 2 dates from the same members table

The project I'm working on right now needs a reference system (currently they have 50K members). I decided to add ref and ref_id field in members table.
Structure of members table;
id (int auto),
admin (enum (1,0)),
ref (enum (1,0)),
ref_id (int),
country_id (int),
city_id(int),
town_id(int),
totalRef (int),
fullName (varchar),
registrationDate (datetime)
I would like to list referers data which has new members between 2 dates. I wanted to provide a bit more details so I also tried to add country, city, and town in the query. I tried following query but I don't think this is a good approach to go with considering it takes really long time to load ;
SELECT m.id, m.fullName, m.country_id, m.city_id, m.town_id, m.totalRef,
(select name from country where country.id = m.country_id) as countryName,
(select name from city where city.id = m.city_id) as cityName,
(select name from town where town.id = m.town_id) as townName,
(select count(id) from members where members.ref_id = m.id AND ref_id > 0 AND registrationDate BETWEEN '2011.11.04 00:00:00' AND '2011.11.04 23:59:59') as newRef
FROM members as m
WHERE
m.country_id = '224' AND
m.city_id = '4567' AND
m.town_id = '78964' AND
m.admin = '0' AND
m.ref = '1'
ORDER BY newRef DESC
LIMIT 0, 25
I will be glad if you could help me about this problem. Thank you in advance.
Something like this -
SELECT
m.id,
m.fullName,
m.country_id,
m.city_id,
m.town_id,
m.totalRef,
cnt.name countryName,
ct.name cityName,
t.name townName,
m2.newRef
FROM members as m
LEFT JOIN country cnt
ON cnt.id = m.country_id
LEFT JOIN city ct
ON ct.id = m.city_id
LEFT JOIN town t
ON t.id = m.town_id
LEFT JOIN (
SELECT ref_id, COUNT(id) newRef FROM members
WHERE ref_id > 0 AND registrationDate BETWEEN '2011.11.04 00:00:00' AND '2011.11.04 23:59:59'
GROUP BY ref_id
) m2
ON m2.ref_id = m.id
WHERE
m.country_id = '224' AND
m.city_id = '4567' AND
m.town_id = '78964' AND
m.admin = '0' AND
m.ref = '1'
ORDER BY
newRef DESC
LIMIT
0, 25;