I have a restful web service that has an endpoint for getting all sub-resources of a single resource (say, for a contrived example, selecting all pets for a certain pet store by making a GET request to http://www.url.com/stores/1234/pets).
This web service is highly concurrent, and gets hit with many requests at once. I'd like to write a single query (for a MySQL database) that allows me to retrieve the number of pets associated with a store, and to be able to distinguish if a store with that id doesn't exist.
That is, if a store with that id exists, but there are no pets from that store, it should return one thing (an empty list); but if that store id does not exist, then raise an exception. Can someone please help me write this SQL query?
Here is some pseudo-sql
CREATE TABLE STORE(
STORE_ID <- AUTO GENERATED PK
.....
)
CREATE TABLE PET(
PET_ID <- AUTO GENERATED PK
STORE_ID <-FOREIGN KEY POINTING TO STORE TABLE
)
STORE|PET
1 | 1,2
2 | NO PETS
I would want the query to be able to give different outputs for querying for store id 2 (where the store exists, but no pets associated with it) and querying for pets for store 3 (where no store with id 3 exists).
Please avoid multiple queries (even using locks/transactions) or stored procedures if possible.
Additional notes:
For the web service, at first I had made a query to check that a store exists with that id, if it doesn't, then throw an exception (that turns into a 404). However, in a highly concurrent application, we could check that the store exists, and find that it does. However, there could be another request that deletes that store before our next query (to retrieve all pets) occurs. In this case, I would like to be true to RESTful principles and return a 404, rather than an empty list. Hence, I would need to distinguish between the two cases. I know how to do this with a transaction and a lock (select for update), but I'd prefer to do this another way if possible.
select s.store_id, count(p.store_id)
from store as s
left join pet as p -- get a row if the store has no row in PETS
on s.store_id = p.store_id
where s.store_id = 9
group by s.store_id -- get no row even if store doesn't exist.
The LEFT join assures that the store is returned even if it has no rows in PETS, the count(p.store_id) returns 0 (caution: don't use count(s.store_id), this would result in 1).
And the GROUP BY utilizes the fact that a COUNT with Group By returns no result if there's no input row (caution: without Group By the Count will return a row).
Related
I'm saving a record into a customer table, and the customer table has an customer number that auto increments. I would like to know what is the best way to get that newly generated customer number directly after it was created? For example, let us say the customer table has 3 columns, ID, Name, Surname. I use the following code:
database.tbCustomer.Insert;
database.tbCustomerName.Value := edtName.Text;
database.tbCustomerSurname.Value := edtSurname.Text;
database.tbCustomer.Post;
So once I have done this, I need to get the ID that was created, as I need to use that as a FK in another table. So the easy way I suppose is to just go to the last record in the customer table, but that gives problems if more then 1 person is creating customers.
database.tbCustomer.Last;
but that approach can lead to the wrong ID being returned if more then 1 customer gets created at the same time. So how would I get the customer ID for the customer that I just created? Using Delphi, and storing the data on mysQL, using FireDAC.
Probably what you are looking for is GetLastAutoGenValue. See the documentation
I was hoping someone could help me come up with a query for what I'm looking to do.
I have a website that lists game servers and I'm trying to improve my search system a bit.
There's three tables of interest; servers, version_taxonomy and category_taxonomy. The taxonomy tables contain two columns, one for a server ID and one for a version/category ID, where associations between a server and it's supported versions and categories can be made.
Up till now, I've been joining both taxonomy tables to the server table and be looking up servers for one version and one category, it's been working fine. However I'm looking to allow the search of a server that has multiple categories at the same time.
I've made an image to try and illustrate what I'm looking to do:
Say I'm looking for a server that has both categories 5 and 12 - Based on the table on the left that would be servers 1 and 3. But how would that be in a query? And how would I use that query to later get and work with the rest of the server data (JOIN like I'd normally do?)
Hopefully that makes sense! Looking forward to your responses.
Assuming I understand the question:
Join the two tables then count the distinct values of category ID while limiting by them. Distinct is not be needed if you can guarantee the uniqueness of serverID, categoryID from table A and a 1:1 relationship to server taxonomy which would be true if you always limit by 1 and only 1 version...
SELECT A.ServerID, count(A.CategoryID) CatCnt
FROM A
INNER JOIN B
on A.ServerID = B.ServerID
WHERE A.CATEGORYID in (5,12)
and B.Version= 1.16
GROUP BY A.ServerID
HAVING count(distinct A.CategoryID) = 2
The category ID could be parameter passed in as well as the count distinct as you know both values.
This could be used as a CTE or as a inline derived table as a source then join in to get the addiontal data; or left join in the desired data assuming it's a 1:1 relationship.
If you want a working example: post DDL for table and SQL to create sample data and I'll put something in https://rextester.com/.
I am asking this question which is to teach myself of using correct approach in a certain scenario than any how-to-code problem.
Since I am self taught student and haven't used relational tables before. With search and experiment, I have come to know the basic concept of relations and their usage but I am not sure if I am still using the correct approach while using these tables.
I do not have any official teachers so only place I can ask troubling questions is here with you guys.
For example, I have written a little code where I have 2 tables.
Table-1 is doctors which has an id (AI & Primary) and names table of varChar.
Table-2 is patient_recipts which has a doctor_name table of tinyInt
names table hold the name of the doctor
doctor_name table holds the corresponding id from doctors table
name and doctor_name are related to each other in database
Now when I need to fetch data from patient_recipts and display doctor's name, I will need to INNER JOIN doctor table, compare the doctor_name value with id in doctor table and get the name of the doctor.
The query I will use to fetch patients of a certain doctor, is something like,
$getPatList = $db->prepare("SELECT *
FROM patient_recipts
INNER JOIN doctor ON patient_recipts.doctor_name = doctor.id
WHERE dept = 'OPD' AND date_time = DATE(NOW())
ORDER BY patient_recipts.id DESC");
Now if I were to INSERT an action log entry in some other processor file, it would be something like (action and log entry),
$recipt_no = $_POST['recipt_no'];
$doctor_name = $_POST['doctor_name']; //this hold id(int) not text
$dept = $_POST['dept'];
$patient_name = $_POST['patient_name'];
$patient_tel = $_POST['patient_telephone'];
$patient_addr = $_POST['patient_address'];
$patient_age = $_POST['patient_age'];
$patient_gender = $_POST['patient_gender'];
$patient_fee = $_POST['patient_fee'];
$logged_user = $_SESSION['user_name'];
$insData = $db->prepare("
INSERT INTO patient_recipts (date_time, recipt_no, doctor_name, dept, pat_gender, pat_name, pat_tel, pat_address, pat_age, pat_fee, booked_by)
VALUES (NOW(),?,?,?,?,?,?,?,?,?,?)");
$insData->bindValue(1,$recipt_no);
$insData->bindValue(2,$doctor_name);
$insData->bindValue(3,$dept);
$insData->bindValue(4,$patient_gender);
$insData->bindValue(5,$patient_name);
$insData->bindValue(6,$patient_tel);
$insData->bindValue(7,$patient_addr);
$insData->bindValue(8,$patient_age);
$insData->bindValue(9,$patient_fee);
$insData->bindValue(10,$logged_user);
$insData->execute();
// Add Log
write_log("{$logged_user} booked OPD of patient {$patient_name} for {$doctor_name}");
OUTPUT: Ayesha booked OPD of patient Steve for 15
Now here the problem is apparent, I would need to execute the above mentioned fetch query yet again to get name of the doctor with ID comparison and bind the ID 15 to Doctor's name before calling the write_log() function.
So this is where I think my approach has been wrong altogether.
One way could be using actual doctor name in patient_recipts rather than ID
but this would i, in first place, kill the purpose of learning related tables and keys, learning design scenarios and troubleshooting.
Please help so I can understand and implement a better approach for days to come :)
Your table structure is correct, it's considered best practice to use the ID as the foreign key in other tables. If you want to include the doctor's name in the log message, you do have to do another SELECT query. A query like
SELECT name
FROM doctor
WHERE id = :doctor_id
is not very expensive.
But you can simply live with the log file only containing IDs. Look up the doctor's name later if you need to find out which doctor a particular log message is referring to.
BTW, when you use PDO, I recommend you use named placeholders (as in my example above) rather than ?. It makes the code easier to read, and if you modify the query to add or remove columns you don't have to change all the placeholder numbers.
I have a database that allows a list of businesses.
The Master table has the customer aka businesses details:
id
firstname
lastname
tradingname
storeaddress
state
postcode
In a table called Otherstores, I have the following:
master_id
store_id
storeaddress
state
postcode
phonenumber
What I now need to do is a PHP script that allows me to show all the stores in a list function but here is the catch:
I only want to show 8 stores from different types of categories so they are random.
However I then need it NOT to show a store twice on the same search.
I need it to make the sub-stores aka Otherstores also be randomly added into the query so that they are seeable as well.
I wondering the best way to do this.
WHY I DON'T HAVE ANY CODE:
It's tough to show you code as my idea was to do a left join or INNER join and limit it to id 1.
However I know that won't work because I would need to be able to join them together some how, but I want each sub store to be like its a master store so if I join it to the master table I can't see that working, and instead you will just get errors.
I have 3 tables in Mysql 5.
Table Client: ID, Username, Password.
Table Client_Data: ID, Dataname
Table Client_Client_Data: client_id, Data_id, Value
The idea is that I can have the user of this software determine which information he wants to get from his clients. The Client_Data table would typically be filled with "First Name", "Last Name", "Address" and so on. The third table will join the tables together. An example:
Client: ID=1 Username=Bert01 Password=92382938v2nvn239
Client_Data: ID=1 Dataname=First Name
Client_Client_Data: client_id=1 data_id=1 value=Bert
This would mean that Bert01 has a first name "Bert" when joining the tables in a select query.
I'm displaying all this in a table where the columns are the DataName values (if you lost me here: the headers would be like "First Name", "Last Name" and so on). I want to be able to sort this data alphabetically for each column.
My solution was to use 2 queries. The first one would collect the data with WHERE Client_Data.Dataname = $sortBy ORDER BY Client_Client_Data.value and the second query would then collect the other data with WHERE Client.ID = 1 OR 2 OR 3 containing all of the ID's collected in the first query. This is working great.
The problem that has been playing in my mind for a long time now is when I want to search my data. This would not be too hard if it weren't for the sorting. After the search has been done the table would contain the results, but this table has to be sorted the same way as before.
Does anyone have any idea on how to do this without bothering the webserver's memory by looping through potentially thousands of clients? (meaning: i want to do this in Mysql).
If your solution would require altering the tables without losing the capability of storing this kind of data: that would be no problem.
you could relocate the looping. make a select from all the datatypes
Select * from Client_Data
then use that info to build a query like so (psuedo code)
orderby = "name"
query = "select *"
foreach(datatypes as dt){
query += ",(select d.value from Client_Client_Data as d where d.data_id="+dt.ID+" and d.client_id=cl.ID) as "+dt.Dataname
}
query = "from Client as cl order by "+orderby;
this will result in a table with all the available datatypes transfered into a column and the corresponding value connected to the correct client trough d.client_id=cl.ID
whereas cl.ID refers to the main queries client id and matches it against Client_Client_Data.client_id
now beware i am not entirely sure about the subqueries being more efficient. would require some testing