SQL Query from Three Related Tables - mysql

I am having trouble putting together a MySQL query.
I have a database with three tables: Location, LocationTypes, and LocationStatus.
I need to retrieve the total of each type of status for each location. So, I the result should look like this:
id_LocationType, status, total
1, open, 200
1, closed, 100
2, open, 400
2, closed, 500
The query should accessing three tables.
LocationTypes --< Locations --< LocationStatus
I came up with this:
SELECT status, COUNT(DISTINCT id_Locations)
FROM LocationStatus
GROUP BY status
But this does not give me the status by LocationType, It gives me the total status for each type of status, combining all LocationTypes:
status, value
open, 600
closed, 600
It doesn't distinguish between LocationType (from the LocationType table). Also, it counts all status values. However, as a location can have multiple status records (ie. a record is created each time the status is updated), I want to count only the most recent status update as the valid one.
The schema looks like this:
Locations Table
-- id (PK)
-- id_LocationType (FK to LocationTypes Table)
-- name
-- date_created
-- date_modified
LocationTypes Table
-- id (PK)
-- nameLocationType
-- date_created
-- date_modified
LocationStatus Table
-- id (PK)
-- id_Location (FK to Location Table)
-- status
-- date_created
-- date_modified
So, it is linked like this:
LocationTypes --< Locations --< LocationStatus
How do I put together this query?

You need to group the result by both LocationType and status. Try this:
SELECT id_LocationType, status, COUNT(DISTINCT id_Locations)
FROM Locations join LocationStatus
ON Locations.id = LocationStatus.id_Location
GROUP BY id_LocationType, status

I hope so this query will be helpful for you.
select lt.id as id_LocationType , ls.status,count(*)
from LocationTypes as lt join Locations as l join LocationStatus as ls
on lt.id=l.id_LocationType and on l.id=ls.id_location
Group by lt.id,ls.status.

Related

MySql query to fetch data in 3rd table from 1st table

I have three tables.
Fee table which contains Fee_id and Fee_name...
Session table which contains session_id and fee_id(foreign key).
classfee_charge table which contains session_id(foreign key),class_id(fo[![enter image description here][1]][1]reign key) and amount.
I have to store amount of fee classwise in classfee_charge table.
How to fetch fee_name in classfee_charge table?
You can put the current session_id (1 in the code bellow) after you group by Fee_name to have the aggregate data for each kind of expense :
SELECT
Fee.Fee_id,
Fee.Fee_name
FROM
Fee, Session
WHERE
Fee.Fee_id = Session.Fee_id
AND
Session.session_id = 1
GROUP BY
Fee.Fee_name
The result :
Fee_id Fee_name
1 Expense
SQL Fiddle for more details
SELECT f.Fee_name from fee f
INNER join session s
on s.Fee_id=f.Fee_id
INNER join classfee_charge cf
on cf.session_id=s.session_id

Count rows in a table according to criteria in another table

I have two tables, "Booking" and "Sports facility". A user can book a given sports facility in a given hour (each facility/hour has a unique ID, like for example "Swimming Pool" at "9:00" will have a different ID than at "10:00", and also a different one in a different day).
Booking has the following columns...
[ ID (PK), ID_FACILITY, ID_USER, DATE, HOUR, PAID, PAYMENT_METHOD ]
Facility has the following columns...
[ ID (PK), NAME, STATE, PRICEPERHOUR, DATE, HOUR ]
The problem that I have is that I don't know how to compare the values between those two tables. I would like to count the entries in the "Booking" table but checking that the ID_FACILITY value is equal to a given one, like "Swimming Pool" for example, and also check that the hour is "9:00" for example.
The wrong query I got so far is this one...
select count(*) as totalbookingcriteria from public.booking, public.facility where hour = '9:00' and name = 'Swimming Pool'
What you need is just an INNER JOIN for your tables, so
select count(*) as totalbookingcriteria
from public.booking b
INNER JOIN public.facility f
ON ( b.ID_FACILITY = f.ID )
where f.hour = '9:00'
and f.name = 'Swimming Pool'

how do I do a join with a polymorphic has_many 'through'?

I have the following data structure (simplified), which includes a polymorphic association in the purchases table (MySql)
Purchases
product_type (this refers to a table, book, dvd, shirt,...)
product_id (this refers to the product's id in the table in product_type)
amount
Books
category_id
DVDs
category_id
Shirts
category_id
Categories
name
I would like to select categories from the db, ordered by total sales (purchases.amount). How can I do this with joins and aggregate functions?
I made a sqlfiddle for this:
http://sqlfiddle.com/#!2/74705
Polymorphic IDs are similar to inheritance structures in a DB, in that to query across all of them often you have to use a union.
However, if your Books/Shirts/DVDs tables have no common columns, it makes absolutely no sense to query across them. If they did have common columns, probably makes more sense to pull that out into a parent Product table that acts as a base for the others.
If you for example wanted total sales per product, you'd just do
Select sum(amount), product_id -- or product_type if you wanted sales per type
From Purchases
Group By product_id -- product_type
You could pull in product titles like this:
Select sum(p.amount), p.product_id, b.Title
From Purchases p
Inner Join Books b on b.category_id = p.product_id
Group By product_id
Union
Select sum(p.amount), p.product_id, s.Title
From Purchases p
Inner Join Shirts s on s.category_id = p.product_id
Group By product_id
It might be more performant to do the union first, then the group by outside of that.
My experience with inheritance hierarchies is that you should do whatever you can to avoid unions. They are a pain to query, result in non-DRY queries, and perform badly. This means pulling out commonalities such as Title into a proper normalized structure to avoid having to query across the concrete/derived types. This is essentially what Wrikken mention in comments "Usually, a better way for this would be a Products table with the shared information types"
Here's an example of how you could achieve this using temporary tables. The principle is that you create a "temporary" table and populate it with the data that you want, in the structure that you want, when your query is run. You can modify the data, and return it at the end and the table is then destroyed when the connection closes.
Anyway - you can set up your temporary table like with the schema combining a new unique, product id, product type, product name and category id. You then populate the table with a union query between all the records in your books, dvds and shirts tables to suit that format:
-- delete the temp table in case it's still there
DROP TEMPORARY TABLE IF EXISTS all_products;
-- create your new temp table
CREATE TEMPORARY TABLE IF NOT EXISTS all_products (
new_id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
product_id INT(11) NOT NULL,
product_type VARCHAR(50) NOT NULL,
product_name VARCHAR(128) NOT NULL,
category_id INT(11) NOT NULL
) AS (
-- populate it with unioned queries
SELECT * FROM (
(SELECT NULL AS new_id, id AS product_id, 'book' AS product_type, `name` AS product_name, category_id FROM books)
UNION ALL
(SELECT NULL AS new_id, id AS product_id, 'dvd' AS product_type, `name` AS product_name, category_id FROM dvds)
UNION ALL
(SELECT NULL AS new_id, id AS product_id, 'shirt' AS product_type, `name` AS product_name, category_id FROM shirts)
) AS temp -- an alias is required - doesn't matter what it is
);
Now you can access the data in this table as you normally would. The benefit of using temporary tables is that you don't have to change your database structure - in your case it's maybe not ideal to have it set up the way it is, but doing something like this could help you to select data efficiently without changing it.
To select data, you could use a query like this:
SELECT
purchase.*,
product.product_name,
category.name
FROM purchases AS purchase
-- get your product info from the temp table
INNER JOIN all_products AS product
ON (product.product_id = purchase.product_id AND product.product_type = purchase.product_type)
-- get the category name
INNER JOIN categories AS category
ON (category.id = product.category_id)
You said you want to order your results by amount, etc. Add your conditions and ordering to the query above as you want to.
Example output:
id product_id product_type amount product_name name
1 1 book 10 how to educational
2 1 dvd 10 video how to educational
3 1 shirt 10 tshirt clothes
These can actually be very efficient to run, for example running this on my local server is executing in 0.001s - it will scale very nicely. I would've set you up a fiddle for this, but SQLFiddle doesn't seem to support temporary tables very well. Just run the CREATE and SELECT statements at the same time on your local server to see.

Join table on pattern match

I have been struggling with this for several hours, so any feedback or advise is very welcome.
I have three tables:
users
id name email
1 test test#test.com
2 test2 test2#test.com
pets
pet_id pet_name user_id
1 sam 2
2 sally 1
transactions
trans_id custom
1 1
2 pid2
3 pid1
OK, what I would like to do is get transaction data relating to the user. So in the 'transactions' table 'custom' value 1 would relate to 'users' with the id. Thats the simple bit...
'Transactions' with 'pid' relate to the pets id, so 'pid2' relates to sally, whose user is user id 1. So I need to join the transaction table when custom relates to the user id or if its prefixed with 'pid' and the appending value relates to the 'pet_id'.
Here's an example of the result I would like:
Transactions relating to user_id 1:
trans_id 1, custom 1
trans_id 2 custom pid2 (this is because the pets owner is user_id 1)
Here is where I am with my attempt at the moment:
SELECT users.*, transactions.*
FROM users
LEFT JOIN transactions on users.id = transactions.custom
This is where I'm falling over:
SELECT users.*, transactions.*
FROM users
LEFT JOIN pets ON pets.user_id = user.id
LEFT JOIN transactions on (users.id = transactions.custom
OR pets.pet_id REGEXP '^pid(transactions.custom)')
If you can't change the table design and the prefix pid is fixed you could use
OR (
pets.pet_id = SUBSTR(transactions.custom, 3)
AND SUBSTR(transactions.custom, 1 FOR 3) = 'pid')
see documentation to SUBSTR and because MySQL automatically converts numbers to strings as necessary, and vice versa, see: http://dev.mysql.com/doc/refman/5.7/en/type-conversion.html
You HAVE to refactor Your DB. Current structure will guarantee of speed problems.
Table transactions should looks like
CREATE TABLE transactions
(
id Int NOT NULL, (id of transaction)
pet_id Int, (can be null)
user_id Int (can be null)
other columns here...
)
;

Select from 3 joined tables with condition?

I have four tables:
request:
id----salesid--custid---serial-----active
=======================================
1-------2-------1--------13221-------1
2-------1-------2--------15422-------1
3-------1-------3--------11233-------1
4-------2-------1--------11342-------1
salesid is foreign key from emp table and we don't need any thing from employee table except the emp id so its not important to show its details
custid is foreign key of customer id
serial is the serial of that request
active is flag for delete
requestcondition:
id-----requestid-----requestcondition
======================================
1--------1-------------pending
2--------1-------------installation pending
3--------2-------------pending
4--------1-------------completed
and
customer:
id------name
============
1-------aaaa
2-------bbbb
3-------cccc
I want to select last condition added for specific request and the name of the customer and request serial according to the salesid column
Try
SELECT Id=scope_identity();
Or
LAST_INSERT_ID();
Through this you can get the last inserted Id and after getting last condition, Implement joins.Read about it on this Link.
select r.id, rc.id, c.name, r.serial, rc.requestcondition
from request r
inner join customer c on c.id=r.custid
inner join requestcondition rc on rc.requestid=r.id
inner join (
select max(id) as rcid
from requestcondition
group by requestid
) latest on latest.rcid=rc.id
This will give the latest status of all requests. If you need just the latest status for one request, you would need to add a where clause at the end.