Creating a frequency table in Access VBA - ms-access

I have a table where different participants are given multiple boxes of medicines on multiple days. I am trying to create a frequency table showing how much medicines have been distributed by the number of boxes to the participants.
The result I'm looking for is -
2 boxes = 1 (since only Lynda got a total of 2 boxes), 4 boxes = 2 (since Ryan and Rinky both got a total of 4 boxes after adding up the medicine boxes)
Please let me know what approach would be the best in this case.
Thanks for your help.
-Nams

I think you want:
SELECT t.SumOf, Count(t.[PARTICIPANT ID]) AS CountOf
FROM (SELECT Table1.[PARTICIPANT ID], Sum(Table1.MEDICINE_BOX) AS SumOf
FROM Table1
GROUP BY Table1.[PARTICIPANT ID]) AS t
GROUP BY t.SumOf;
Where table1 is the name of your table.

If your table is like this:
medicine_dispense
participantID date amount_boxes
ABC 8/29/12 1
ABC 8/30/12 2
XYZ 8/29/12 1
XYZ 8/30/12 1
then a query like this:
select
amount_boxes, count(participantID)
from
medicine_dispense
should work

I'll use generic SQL. You can paste SQL into Access queries in SQL view. (You might have to delete the CHECK() constraint.)
create table participant_meds (
participant varchar(10) not null,
distribution_date date not null default current_date,
num_boxes integer not null check (num_boxes > 0),
primary key (participant, distribution_date)
);
insert into participant_meds values ('Ryan', '2012-02-03', 1);
insert into participant_meds values ('Ryan', '2012-06-07', 3);
insert into participant_meds values ('Rinky', '2012-02-28', 4);
insert into participant_meds values ('Lynda', '2012-03-04', 2);
insert into participant_meds values ('Russ', '2012-04-05', 2);
insert into participant_meds values ('Russ', '2012-05-08', 2);
insert into participant_meds values ('Russ', '2012-06-12', 2);
Resulting data, sorted, for copy/paste.
participant distribution_date num_boxes
Lynda 2012-03-04 2
Rinky 2012-02-28 4
Russ 2012-04-05 2
Russ 2012-05-08 2
Russ 2012-06-12 2
Ryan 2012-02-03 1
Ryan 2012-06-07 3
This query gives you the total boxes per participant.
select sum(num_boxes) boxes, participant
from participant_meds
group by participant;
6;"Russ"
2;"Lynda"
4;"Ryan"
4;"Rinky"
Use that query in the FROM clause as if it were a table. (I'd consider storing that query as a view, because I suspect that the total number of boxes per participant might be useful. Also, Access has historically been good at optimizing queries that use views.)
select boxes num_boxes, count(participant) num_participants
from (select sum(num_boxes) boxes, participant
from participant_meds
group by participant) total_boxes
group by num_boxes
order by num_boxes;
num_boxes num_participants
--
2 1
4 2
6 1

Related

MySQL find double bookings in hotel database

I need help with an assignment for school. I've only been doing SQL for 2 months and can't figure this out. My teacher gave me some hints about using self joins. The database has 3 more tables, but I don't think they are needed here.
The assignment is to write a query that will help hotel staff find double bookings (same room, same date). I've made a test database that has a double booking to control the query.
drop database if exists hoteltest;
create database hoteltest;
use hoteltest;
create table Roomreservation(
ResNr int not null,
RoomNr int not null,
FromDate date not null,
ToDate date not null,
primary key (ResNr, RoomNr, FromDate)
);
insert into Roomreservation
values
(51, 102, '2008-12-05', '2008-12-07'),
(51, 103, '2008-12-05', '2008-12-07'),
(51, 104, '2008-12-05', '2008-12-09'),
(52, 201, '2008-12-05', '2008-12-14'),
(53, 102, '2008-12-04', '2008-12-10');
select * from Roomreservation;
Does anyone have a good and easy solution for this?
Honestly, I'm kinda stuck, I've been trying different solutions with concat_ws and the dates but with no results.
You need to check for the range of Fromdate and todate. For this, you can use between. You also exclude the same row from the join. And you only extract one from the join since AxB will lead to BxA on the join. Therefore :
SELECT t1.* FROM Roomreservation t1
JOIN Roomreservation t2 ON t1.ResNr<>t2.ResNr AND t1.RoomNr=t2.RoomNr AND (t1.FromDate BETWEEN t2.FromDate AND t2.ToDate OR t2.FromDate BETWEEN t1.FromDate AND t1.ToDate);
You should use an auto-incremented primary key, such as something as the resnumber but unique.
The idea is to join the table on itself, looking for 2 records where the ResNo is different, the RoomNr is the same, and either the FromDate or the ToDate is between the other record's FromDate-ToDate.
I could write the query for you, but i believe in self-learning, especially for school assignments (rather than actual work).
NOTE: don't get lazy when it comes to naming, e.g. ResNr -> ReservationNumber
will save you more time in the future when reading queries
There is a, or at least a, double booking on the following dates:
RoomNr
d
count(*)
102
2008-12-04
2
102
2008-12-05
2
102
2008-12-06
2
102
2008-12-07
2
102
2008-12-08
2
102
2008-12-09
2
102
2008-12-10
2
102
2008-12-11
2
102
2008-12-12
2
102
2008-12-13
2
102
2008-12-14
2
WITH recursive dates as (
select min(FromDate) as d FROM Roomreservation
union all
select adddate(d, INTERVAL 1 day)
from dates
where d<(select max(ToDate) from Roomreservation)
)
SELECT
RoomNr,
d,
count(*)
FROM Roomreservation
CROSS JOIN dates
GROUP BY
RoomNr,
d
HAVING count(*) >1
ORDER BY d, RoomNr
The WITH recursive.... is creating a table (dates) with all dates from the lower value of FromDate to the highest value of ToDate.
And then it's just a matter of counting how many reservation there are on that day, for that RoomNr.
see: DBFIDDLE

How to update and insert into db with #transactional on db query

I have one table warehouse_item_mapping
having columns - warehouseId, itemId, stockQuantity
warehouse_id item_id stock_quantity
1 123 10
1 234 20
1 345 30
This table is being used as inventory management as maintaining current stock of items in warehouse.
Now if new stock has come with some items including new items with current stock quantity.
Like
1 123 50
1 234 50
1 678 50 (new item)
I have to update the stock quantity in table as sum of existing quantity and current stock quantity. And if mapping not found in table then insert that new mapping.
For this case table should be updated as
warehouse_id item_id stock_quantity
1 123 60
1 234 70
1 345 30
1 678 50
How to do this in single query.
Follow up question in this is
If someone used the current stock from table in between get and update query then how to maintain lock or transactional for whole flow.
INSERT ON DUPLICATE KEY UPDATE is what you need. In combination with pre selected stock_quantity. You need to make item_id PRIMARY KEY.
Now of course you will be using one SELECT and one INSERT INTO statement with PHP ( i guess) variables.
SELECT #stock_quantity:=stock_quantity FROM warehouse_item_mapping WHERE item_id=123;
INSERT INTO warehouse_item_mapping (warehouse_id, item_id, stock_quantity)
VALUES (1, 123, 50)
ON DUPLICATE KEY UPDATE warehouse_id=1, stock_quantity=#stock_quantity+50;
Working example: db-fiddle
RESULT:
Results
Query #1 Execution time: 0ms
warehouse_id item_id stock_quantity
1 123 60
1 234 70
1 345 30
1 678 50
SO lats say you are using PHP:
PHP:
'$item_id' // your new OR old inserting item id.
'$warehouse_id'
'$stock_quantity' // Your NEW stock number.
START TRANSACTION;
SELECT #stock_quantity:=stock_quantity
FROM warehouse_item_mapping WHERE item_id='$item_id';
INSERT INTO warehouse_item_mapping (warehouse_id, item_id, stock_quantity)
VALUES ('$warehouse_id', '$item_id', '$stock_quantity')
ON DUPLICATE KEY UPDATE warehouse_id='$warehouse_id', stock_quantity=#stock_quantity+'$stock_quantity';
COMMIT;
This will try to insert a value only if PRIMARY KEY item-id doesn't exists. If it does it will update other two fields.
EDIT:
I think you are confused what #transaction is, read about it here.
Above is example of one as you asked for it but this can also be done in one query as mentioned in comment. Example:
INSERT INTO warehouse_item_mapping (warehouse_id, item_id, stock_quantity)
VALUES (1, 123, 50) ON DUPLICATE KEY UPDATE
warehouse_id=1, stock_quantity=stock_quantity+50;
Working Fiddle.
PHP:
INSERT INTO warehouse_item_mapping (warehouse_id, item_id, stock_quantity)
VALUES ('$warehouse_id', '$item_id', '$stock_quantity') ON DUPLICATE KEY
UPDATE warehouse_id='$warehouse_id', stock_quantity=stock_quantity+'$stock_quantity';

MySQL update column from another table

I have two tables, I want to grab the total number of skills required for a specific job position and update this table's column with that information.
The tskill column is the one intended for the information to be updated to.
pnum sname
===============
001 cooking
001 cleaning
002 teaching
003 driving
pnum ptitle tskill
===================
001 chef
002 teacher
003 driving instructor
Sample data with relevant columns (tskill is a new added column in POSITIONS which is currently all empty)
INSERT INTO POSITIONS VALUES ( 001, 'chef')
INSERT INTO POSITIONS VALUES ( 002, 'teacher')
INSERT INTO POSITIONS VALUES ( 003, 'driving instructor')
INSERT INTO SNEEDED VALUES ( 001, 'cooking');
INSERT INTO SNEEDED VALUES ( 001, 'cleaning');
INSERT INTO SNEEDED VALUES ( 002, 'teaching');
INSERT INTO SNEEDED VALUES ( 003, 'driving');
Any tips for doing this with the update statement?
You should be able to update the position table using this query (SQLFiddle):
UPDATE POSITIONS
SET tskill = (SELECT COUNT(pnum)
FROM SNEEDED WHERE SNEEDED.pnum = POSITIONS.pnum)
Output:
select * from POSITIONS;
pnum ptitle tskill
1 chef 2
2 teacher 1
3 driving instructor 1
I have edited this answer as per your question. Have a look again. What we are doing here is , we are selecting the count of pnum from sneeded table with the similar pnum and updating to positions table where pnum matches. Hope this helps.
UPDATE position as p
(
SELECT pnum, count(pnum) as skill FROM sneeeded
GROUP BY pnum
) as n
SET p.tskill = n.skill
where p.pnum = n.pnum

INSERT id from other column or NULL if value does not exist

THE DATA
I have a set of data in the following format:
CAR_MAKE TABLE
ID MAKE
1 Ford
2 Tesla
3 Acura
4 Honda
5 Toyota
MAKE_NOTES TABLE
NOTE_ID MAKE_ID MAKE_NAME NOTE
1 1 Ford New QNX-Based Sync System
2 2 Tesla Looking forward to Model 3
3 5 Toyota Updated Corolla 2018
4 Null Ferrari Very Fast and Very Red
I know there's repeating data (make_name) between table 1 and table 2. Let's assume I can't mess with the data. I also can't guarantee that an entry would have been made in car_make first. In such a case make_notes.make_id should be null.
WHAT I'VE DONE SO FAR
What I'm trying to do is INSERT a row into make_notes, inserting null into make_id if it does not exist in car_make, otherwise inserting car_make.id.
This works fine if, make_name exists in car_make... but if I attempt to insert a record with a make_name that does not exist in car_make, no record is inserted (no error is thrown either).
INSERT INTO make_notes (
make_id,
make_name,
note
)
SELECT
id,
'ford',
'New Note'
FROM car_make
WHERE make = 'ford';
I've also tried to use this as a subquery:
SELECT
CASE
WHEN (SELECT EXISTS (SELECT 1 FROM car_make where make = 'Ferrari') = 1)
THEN car_make.id
ELSE null
END AS make_id
FROM car_make;
I have not been albe intergrate it into my main query without throwing an error. As a stand alone query it returns one row for each entry in car_make, as null if 'Ferrari' does not exist, and each id if it does.
QUESTION
How do I create an insert query that will insert into 'make_notes', and insert 'null' if make_name does not exist in car_make, and insert car_make.id if make_name does exist?
I think using an IF() clause should work for your case:
INSERT INTO `make_notes` (
make_id,
make_name,
note
)
VALUES(
IF(
((SELECT COUNT(*) FROM `car_make` WHERE `make` = 'ford') > 0),
(SELECT `id` FROM `car_make` WHERE `make` = 'ford'),
NULL
),
'ford',
'New note'
);
I don't think you can do that with one query. I think you have to run a select to get the ID from the car_make table. Then you do the insert.

Comparing counts across two tables

Given two tables...
tblEvents (fldEventID, fldAECap, fldDWCap, fldWSCap)
tblSignUps (fldSignUpID, fldEventID, fldPosition)
where a value of 1 in fldPosition relates to fldAECap, a value of 2 relates to fldDWCap, a value of 3 relates to fldWSCap; I need to prevent any new records from being inserted into tblSignUps if the count of fldPosition values is equal to each related CAP value. For instance, we may only have 3 allotted positions for fldAECAP and that position value occurs 3 times in fldPosition for that given EventID
The basic logic would come down to...
INSERT INTO tblSignUps
VALUES(NULL,12,3)
WHERE fldWSCap > COUNT(fldPosition(3))
How would I do this?
A way to write the required basic logic is this:
INSERT INTO tblSignUps
select NULL,12,3 from dual
where exists (
select * from tblEvents where
fldEventId = 12 and
fldWSCap > (select count(*) from tblSignUps where fldEventId = 12 and fldPosition = 3)
);
Obviously this works only for the field fldWSCap. The others two require a similar statement where the fld*Cap change according to the fldPosition value.