Mysql select max with threshold - mysql

I have the following 3 tables as part of a car booking system:
CREATE TABLE `b_booking` (
`id` INT(10) NOT NULL AUTO_INCREMENT,
`b_car_id` VARCHAR(15) NOT NULL,
`uc_user_id` INT(11) NOT NULL,
`booking_date` DATE NOT NULL,
`delivery_date` DATE NOT NULL,
`delivery_location` INT(10) NULL DEFAULT NULL,
PRIMARY KEY (`id`),
INDEX `FK_b_booking_b_car` (`b_car_id`),
INDEX `FK_b_booking_uc_users` (`uc_user_id`),
INDEX `FK_b_booking_b_location` (`delivery_location`),
CONSTRAINT `FK_b_booking_b_location` FOREIGN KEY (`delivery_location`) REFERENCES `b_location` (`id`),
CONSTRAINT `FK_b_booking_b_car` FOREIGN KEY (`b_car_id`) REFERENCES `b_car` (`id`),
CONSTRAINT `FK_b_booking_uc_users` FOREIGN KEY (`uc_user_id`) REFERENCES `uc_users` (`id`)
CREATE TABLE `b_car` (
`id` VARCHAR(15) NOT NULL,
`b_carmodel_id` INT(10) NOT NULL,
`day_cost` INT(10) NOT NULL,
`location` INT(10) NOT NULL,
`model_year` SMALLINT(4) NOT NULL,
PRIMARY KEY (`id`),
INDEX `FK_b_car_b_carmodel` (`b_carmodel_id`),
INDEX `FK_b_car_b_location` (`location`),
CONSTRAINT `FK_b_car_b_location` FOREIGN KEY (`location`) REFERENCES `b_location` (`id`),
CONSTRAINT `FK_b_car_b_carmodel` FOREIGN KEY (`b_carmodel_id`) REFERENCES `b_carmodel` (`id`)
CREATE TABLE `b_location` (
`id` INT(10) NOT NULL AUTO_INCREMENT,
`adress` VARCHAR(100) NOT NULL,
`b_postal_zip` SMALLINT(4) UNSIGNED NOT NULL,
PRIMARY KEY (`id`),
INDEX `FK_b_location_b_postal` (`b_postal_zip`),
CONSTRAINT `FK_b_location_b_postal` FOREIGN KEY (`b_postal_zip`) REFERENCES `b_postal` (`zip`)
A given car (b_car) will have an int representing av location where a car can be picked up or delivered. However, this location will change, due to the fact that a customer can pick up the car on one location, and deliver at another. When a customer registers a booking (b_booking), he/she also register at what location the car will be delivered (delivery_location).
I'm however having a lot of problems with what seems to me shouldn't be that hard to do: Lets say a car is at location 1 at the beginning of the month. Then, customer X register a booking for 15-20. and registers a delivery at location 2. Then customer Y wishes to book the same car. So I need a query which takes into account the date and location.
SELECT c.id, c.location, b.delivery_location, MAX(b.delivery_date) FROM b_car c
LEFT JOIN b_booking b ON b.b_car_id = c.id
WHERE b.delivery_date < '2012-11-28' OR b.delivery_date IS NULL
GROUP BY c.id;
I have tried something similar to this. I will pick all cars which has no bookings with the left join, and it will also pick the last booking (the last booking related to given date). The problem is, of course, that the where condition also excludes any booking with a date higher than the provided date. Also, with this solution I'm forced to get both the location (from b_car) and the delivery location (from b_booking), and perhaps evaluate null on the delivery_location with php or something.. which really doesnt seem optimal.
Any good solutions?
Thanks

I am not sure what your question is. I think your question is to find out which cars are available based on the planning in the booking system on a given day. In that case the only info you need is the ids from the cars and the location and according to the booking system.
BTW I would suggest to add time to your table, because you may want to rent out a car the same day. If customer A returns the car before 12:00 you can easily rent out the same car at 13:00 to customer B.
SELECT c.id, b.delivery_location, b.delivery_date FROM b_car AS c
LEFT JOIN b_booking AS b ON b.id = c.id
WHERE b.delivery_date = '2012-11-28' and b.delivery_location = 1;
I simplified you query, because I think this will give you the information you need. If a customer wants to rent a car you need to know if the car is available in the planning at the specified location.
If you want to know the availability between to dates use WHERE b.delivery_date between [start date] AND [end date].
I suspect that the type of car is important for the customer, so perhaps it is better to look for a specific car type available at the location of choice.
Furthermore, I would suggest checking if the car is actual at the desired location at the delivery time.

Related

Select Exchange Rate based on Currency and Date [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 3 years ago.
Improve this question
I've looked around for a solution to this, but at least I was unable to find anything which would at least be similar to my case.
I need to select the exchange rate, based on the date a product was purchased.
Let me try and explain.
I have a table with Currencies:
CREATE TABLE `tblCurrencies` (
`CurrID` int(11) NOT NULL AUTO_INCREMENT,
`CurencySymbol` varchar(1) DEFAULT NULL,
`CurrencyCode` varchar(3) DEFAULT NULL,
`CurrencyDescription` varchar(100) DEFAULT NULL,
PRIMARY KEY (`CurrID`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
A table with Exchange Rates:
CREATE TABLE `tblExchRates` (
`ExcID` int(11) NOT NULL AUTO_INCREMENT,
`CurrKey` int(11) DEFAULT NULL,
`Date` date DEFAULT NULL,
`Exchange` decimal(11,3) DEFAULT NULL,
PRIMARY KEY (`ExcID`),
KEY `CurrKey` (`CurrKey`),
CONSTRAINT `tblExchRates_ibfk_1` FOREIGN KEY (`CurrKey`) REFERENCES `tblCurrencies` (`CurrID`) ON DELETE RESTRICT ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=111 DEFAULT CHARSET=utf8;
And a table with Products (note my products are listed in numbers in the table, which is OK in my case):
CREATE TABLE `tblProducts` (
`ProductID` int(11) NOT NULL AUTO_INCREMENT,
`Contract` int(11) DEFAULT NULL,
`Product` int(11) DEFAULT NULL,
`Type` varchar(100) DEFAULT NULL,
`Currency` int(11) DEFAULT NULL,
`Amount` decimal(10,0) DEFAULT NULL,
`PurchaseDate` datetime DEFAULT NULL,
PRIMARY KEY (`ProductID`),
KEY `Contract` (`Contract`),
KEY `Currency` (`Currency`),
CONSTRAINT `tblShopCart_ibfk_2` FOREIGN KEY (`Currency`) REFERENCES `tblCurrencies` (`CurrID`) ON DELETE RESTRICT ON UPDATE CASCADE,
CONSTRAINT `tblShopCart_ibfk_1` FOREIGN KEY (`Contract`) REFERENCES `tblContracts` (`ContractID`) ON DELETE RESTRICT ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=3155 DEFAULT CHARSET=utf8;
In the Exchange Rates table, as an example, values are set like this:
CurrKey Date Exchange
1 15-01-2017 0.850
1 31-01-2017 0.856
1 02-02-2018 0.918
1 18-02-2018 0.905
2 04-02-2018 1.765
2 14-02-2018 1.755
And so on...
I want to have a query that select a unique exchange rate based on the date a product was purchased and the currency it was purchased.
In other words, as an example, if I have a product that was purchased on 07-02-2018, the query has to select the exchange rate which is valid in the relevant date rage that matches the purchase date and its currency. In this example, the correct exchange rage for a product purchased on 07-02-2018 which has a currkey of 1 would be 0.918
Please note that exchange rates are set on random dates (as per example above).
I managed to make this query, but it is not precise, as it sometimes returns two or more results (due to the 10 days range I set), whereas I only need 1 result:
SELECT
tblExchRates.Exchange
FROM
tblCurrencies
INNER JOIN tblExchRates ON tblExchRates.CurrKey = tblCurrencies.CurrID
WHERE
tblCurrencies.CurrencyCode = "EUR" AND
tblExchRates.Date BETWEEN (tblProducts.PurchaseDate - INTERVAL 10 DAY) AND (tblProducts.PurchaseDate + INTERVAL 10 DAY)
For a fairly simple solution you can do
SELECT
p.*
,(SELECT TOP 1 er.Exchange
FROM tblExchRates AS er
INNER JOIN tblCurrencies AS c ON er.CurrKey = c.CurrID AND c.CurrencyCode = 'EUR'
WHERE er.Date <= p.PurchaseDate
ORDER BY er.Date DESC) AS ExchangeRate
FROM
tblProducts AS p
Another option, if you have control over the schema then changing your exchange rates table to have a DateFrom and DateTo rather than just a date would then mean you can simply find the correct exchange rate using the BETWEEN keyword.
I am a beginner myself so no guarantees on correctness. I believe you have to use a certain application programming language along with SQL, for example PHP. Still, I will outline the basic steps I would take.
1. Assign the purchase currency ID and purchase date to variables using a simple SELECT statement. Assume I give the ID to targetID and date to targetDate.
2. SELECT MIN(Date) FROM tblExchRates WHERE Date <= targetDate AND CurrKey =targetID; //Select most recent date whose range includes the date of purchase among the matching currency IDs. Assign the result of this statement to another variable. Assume I used the variable dateRange.
3. SELECT Exchange FROM tblExchRates WHERE Date = targetDate; //Find the exchange rate of the selected date.
Note that there are many ways to do this. For example, you could use table JOINS (refer to this link: https://www.w3schools.com/sql/sql_join.asp ) or select columns from different tables in just one SQL statement (refer to this Stack overflow question: MySQL Select all columns from one table and some from another table). Last, you can use SQL to create variables (refer to this question: Set the variable result, from query) and then perform operations.

Querying of off a query MYSQL

I'be been googling around about nested queries but can't find anything that I can grasp about how to go about this particular operation.
First, I'll show you my DB schema
CREATE TABLE slsemp
( empID char(4) NOT NULL,
empname varchar(50) NOT NULL,
prtime enum('yes','no') NOT NULL, # we can only accept yes or no values to the part-time employee indicator
RegionID char(2) NOT NULL, # enums are often used for boolean values in a BD
PosID char(4) NOT NULL,
PRIMARY KEY (empID),
FOREIGN KEY (regionID) REFERENCES region (RegionID),
FOREIGN KEY (PosID) REFERENCES slspos(PosID));
# create the sales transactions table
CREATE TABLE slstranx
( tranxID int(10) NOT NULL AUTO_INCREMENT, #starts at a certain number, then increments accordingly
empID char(4) NOT NULL,
ProdID char(3) NOT NULL,
Qty int(5) NOT NULL,
Unitprice Decimal(5,2) NOT NULL, # please note we are capturing the unit price at the transactional level in this case
SAmt Float(10,2), #store up to 10 (TOTAL?) with 2 digits past decimal point
SalesDate date, # 'date' data type is organized as follows YYYYMMDD. You need to make sure that raw data contains the DATE in the YYYYMMDD format
# For example 20150915
PRIMARY KEY (tranxID),
FOREIGN KEY (ProdID) REFERENCES product (ProdID),
FOREIGN KEY (empID) REFERENCES slsemp (empID));
Now, I want to find employees that are in the west region that haven't made any sales. I figured I'd do this via a left outer join between the two tables then query the resulting table based off of a null tranx ID. I've got it most of the way there, here's my query:
SELECT e.empID, t.tranxID, e.RegionID
FROM slsemp e LEFT OUTER JOIN slstranx t ON e.empID=t.empID
WHERE e.RegionID='WS'
My question is, how do I query based of the criteria of this resultant table. If I could do that, I simply would need a selection with criteria of slstranxID=null.
You can use left join adding where slstranx.empID is null
select distinct empID, empName
from slsemp
left join slstranx on slsemp.empID = slstranx.empID
where slsemp.RegionID = 'WS'
and slstranx.empID is null
if the column from the table in left join is null mean that don't match .. so don't have sales

Best table/index structure for data with time-varying association

My company uses a number of data acquisition devices (DAQs) to monitor the output of solar panels on a test site. Each DAQ has a unique serial number, and each solar panel has a unique serial number. Occasionally we swap out the panels for new panels, and occasionally the DAQs fail and need to be replaced with new ones with different serial numbers.
My question is, what is the best table structure for queries to see all of the data for a particular solar panel's serial number, given that it can be on different DAQs at different times?
I'm currently using the following table structure:
Table: relationships
id int(11) NOT NULL AUTO_INCREMENT,
daqID char(4) NOT NULL,
dtdateFirst datetime NOT NULL,
dtdateLast datetime NOT NULL,
PanelType varchar(20) NOT NULL,
sgcucode varchar(45) NOT NULL,
serial varchar(15) NOT NULL,
ptype varchar(15) NOT NULL,
PRIMARY KEY (id),
KEY daqID (daqID),
KEY gcuidx (sgcucode),
KEY serialidx (serial),
KEY fullidx (sgcucode,daqID,serial,dtdateFirst,dtdateLast)
) ENGINE=InnoDB AUTO_INCREMENT=135 DEFAULT CHARSET=utf8
Table: data
id int(11) NOT NULL AUTO_INCREMENT,
dtdate datetime NOT NULL,
daqID char(4) NOT NULL,
Varray text NOT NULL,
Iarray text NOT NULL,
Iavg float NOT NULL,
Pmp float NOT NULL,
PRIMARY KEY (id),
UNIQUE KEY id_UNIQUE (id),
UNIQUE KEY dupliData (dtdate,daqID,Iavg),
KEY idxDaqDate (daqID,dtdate),
KEY idxDate (dtdate),
KEY idxPmp (Pmp)
) ENGINE=InnoDB AUTO_INCREMENT=14027571 DEFAULT CHARSET=utf8
The "relationships" table matches the daqID to a panel serial number for a given time span (from "dtdateFirst" to "dtdateLast"). So the important columns for this question are: daqID, dtdateFirst, dtdateLast, serial. Also of some importance is the "sgcucode". This column indicates which test site the modules are on. It is used by a dashboard so that we can cycle through various sites which log data to the same table.
Data is constantly being logged to the "data" table from the DAQ devices. I then use the relationships table to correlate the serial number of the solar panel with the correct daqID for the time in question.
By far the most common query is to collect all of the data in the "data" table for a given day, and display it in a dashboard (shown below).
This is the query I use to do this:
SELECT relationships.serial as title, dtdate as time , Pmp as Value, relationships.ptype as type
FROM data INNER JOIN (relationships) ON (relationships.daqID=data.daqID)
AND dtdate BETWEEN DATE_FORMAT(example_date, '%Y-%m-%d 05:00:00') AND DATE_FORMAT(example_date, '%Y-%m-%d 21:00:00')
WHERE relationships.dtdateFirst <= dtdate
AND relationships.dtdateLast >= dtdate
AND sgcucode="example_code";
Given these conditions, is this the best solution? I probably have redundant indexes, I am still learning about database design, so any suggestions for improvement would be greatly appreciated!

A record self referencing in mysql

I've got a self referencing table in mySql, a table of managers and employees.
CREATE TABLE `employee` (
`employee_id` BIGINT(10) NOT NULL AUTO_INCREMENT,
`firstname` VARCHAR(50) NULL DEFAULT NULL,
`lastname` VARCHAR(50) NULL DEFAULT NULL,
`manager_id` BIGINT(20) NULL DEFAULT NULL,
PRIMARY KEY (`employee_id`),
CONSTRAINT `FK_MANAGER` FOREIGN KEY (`manager_id`) REFERENCES `employee` (`employee_id`)
)
I want to know if it's conceptual correct that a record references himself. I explain better: some employees has relationships with some manager, so for example employees 3,5,7 have relationships with manager 1, but I'd like to create a list to show all record related to manager 1 and also manager 1. So I've tried to make manager 1 have relationships with himself, so manager 1 refers to manager 1.
I've got no errors, but wanna know if this is a correct way to implement this relationship or if I will have problems in future.
Thanks
In my opinion it is not best solution because:
It limits You to have only 2 levels in management hierarchy(only manager and employee) You can't have manager -> leader -> employee
You can't easily print a tree of employees
I practice in bigger company the only person with no one on top would be CEO
Myself I would use this query instead
SELECT * FROM employee WHERE employee_id=1
UNION
SELECT * FROM employee WHERE manager_id=1

Correct database? Does it need altering?

I'm creating a page where I want users to be able to book a seat for an event.
1 user can only book 1 seat
users have no seat selected upon login, first after buying into a spot
Need to able to clear seats table, without loosing anything from user-table (except of course the assigned seats.)
I've created two tables, and since I'm pretty new to mySQL, I wanted to check if this was done correctly:
members-table:
user_id int(8) Not null auto_increment
user_name varchar(30) Not null
user_pass varchar(255) Not null
seat_ID smallint(6) Yes NULL
seats-table
seat_ID smallint(6) No auto_increment
user_id smallint(6) Yes NULL
seat_status tinyint(4) Yes NULL
seat_status tinyint(4) Yes NULL
I've created 2 FK-refs:
ALTER TABLE seats
ADD CONSTRAINT FK_seats
FOREIGN KEY (user_id) REFERENCES members(user_id)
ON UPDATE CASCADE
ON DELETE CASCADE;
ALTER TABLE seats
ADD CONSTRAINT FK_seats
FOREIGN KEY (seat_ID) REFERENCES members(seat_ID)
ON UPDATE CASCADE
ON DELETE CASCADE;
Am I on the right track? Will I be able to progress to a decent final product with this setup? suggestions/improvements? I don't want to start all over in a couple of weeks because the database structure is of poor quality.
First of all I don't see why you're using a second table if any user can only hold one seat at any given time, secondly user_id in seats-table should be the same size as user_id in members table namely int(8), otherwise you won't be able to seat users after a while, third issue is the duplication of seat_status, I suppose that was a mistake or you had another name for it.
In my opinion a better idea is to use a single table if it's a 1->1 mapping and define it as
CREATE TABLE `members-table` (
user_id int(8) not null auto_increment,
user_name varchar(30) not null,
user_pass varchar(255) not null,
seat -- your type choice, should be nullable if not seated
);
Clearing the seats with this config would be as simple as
UPDATE `members-table` SET `seat` = NULL;
CREATE TABLE `seats` (
id int(4) unsigned not null auto_increment primary key,
row int(2) unsigned not null,
col int(2) unsigned not null,
UNIQUE(row, col)
) ENGINE InnoDB;
CREATE TABLE `members` (
user_id int(8) not null auto_increment primary key,
user_name varchar(30) not null,
user_pass varchar(255) not null,
seat int(4) unsigned null,
FOREIGN KEY(seat) references seats(id) on delete set null on update restrict,
UNIQUE(seat)
) ENGINE InnoDB;
You will have to populate the seats database with all available rows and columns, use null on id when inserting to use the auto_increment feature!
Check if a seat is taken
SELECT COUNT(*) AS occupied FROM members WHERE seat = (SELECT id FROM seats WHERE row = :ROW AND col = :COL);
Alternatively use SELECT (1 - COUNT(*)) AS vacant in the query above if it's more conveninent for you.
Find first free seat
SELECT MIN(id) FROM seats WHERE NOT EXISTS( SELECT seat FROM members WHERE seat = seats.id);
Unassign all taken seats
UPDATE members SET seat = NULL;