I need to create a small "application" using ms access 2007. All I need is to create a form that will take care of input/output related to a few db tables.
I created the tables: patients, treatments and labresults.
Primary key in patients table is ID, and it serves as a foreign key in treatments and labresults tables, where it is named patientID.
I also created a form that I mentioned in the beginning of this question. It has multiple tabs. 1st tab opens/creates the patient, and the second one is used to enter data that is to be entered into the labresults table. Same applies to treatments table.
My problem: I added a button to the 2nd tab, and I want to attach an 'action' that will set an INSERT query with values taken from fields (controls) in tab 2, together with ID field taken from tab1 (that corresponds with patient ID from patients table), and then execute a query.
Right now I'm trying to achieve this myself, but with little success. Also, searchin MS site for solution(s) is kind of hard, since it always show results that have 'query' in it :)... And query isn't smt I want to use. (However, I'll accept any solution).
Thx
Tables:
patients
ID - primary key, autogenerated
patientID - internal number of the patient record. I could've used this, but it would complicate my life later on :)
gender
age
dateOfDiagnose
- field names are actually in Serbian, but field names aren't that much important
labtests
ID - primary key
patientID - foreign key, from patients table
... bunch of numerical data :)
There are 2 more tables, but they basically reflect some additional info and are not as important.
My form needs to enable user to enter data about the patient, and then enter several rows in labtests table, as treatment progresses. There are 2 types of treatment, and there is a table related to that, but they only have few fields in it, containint info about the start of the treatment, and the rest is info about lab tests.
It is quite possible to run SQL under VBA, but I suspect that it may be easier to simply set up the form properly. For example, if you set up the ID as the Link Child & Master fields for the lab results subform (which I hope you are using), then it will be filled in automatically. Furthermore, it is also possible to set the default value of a control to the value that was previously entered with very little code. I therefore suggest that you add some notes on what you wish to achieve.
Some further notes based on subsequent comment
From your comments, it appears that you have a minimum of three relevant tables:
Patients
PatientID PK, Counter
Treatments
TreatmentID PK, Counter
PatientID FK
TreatmentTypeID FK
LabResults
LabResultID PK, Counter
TreatmentID FK
PatientID FK <-- Optional, can be got through TreatmentID
LabResultTypeID FK
In addition, you will need a TreatmentTypes table that list the two current treatments and allows for further treatment types:
TreatmentTypes
TreatmentTypeID PK, Counter
TreatmentDescription
You will also need:
LabResultTypes
LabResultTypeID PK, Counter <-- This can be referenced in the LabResults table
TreatmentTypeID FK
There are arguments for PKs other than those suggested, in that you have natural keys, but I think it is easier when working with Access to use autonumbers, however, you will need indexes for the natural keys.
I strongly recommend that all tables also include date created, date modified/updated and a user ID for both. Access does not keep this data, you must do it yourself. Date created can be set by a default value, and if you are using the 2010 version, the other fields can have 'triggers' (data-level macros), otherwise, you will need code, but it is not too difficult. Note also that these same 'triggers' could be used to insert relevant records : Meet the Access 2010 macro designer.
Your form will contain records for each patient and two subforms, Treatments and LabResults. The Treatments subform is bound to the Treatments table having PatientID for LinkChild and Master fields and a combobox for TreatmentTypeID:
Rowsource: SELECT TreatmentTypeID, TreatmentDescription FROM TreatmentTypes
ColumnCount: 2
BoundColumn: 1
After a treatment type is added to the Treatments subform, you can run a query, or run SQL under VBA. You can either use the After Insert event for the form contained by the treatments subform or a button, the After Insert event has advantages, but it is only triggered when the user saves the record or moves from the record, which is the same thing. Working from the treatments subform, and the After Insert event, the SQL would be on the lines of:
''Reference Microsoft DAO x.x Object Library
Set db = CurrentDB
sSQL = "INSERT INTO LabResults (TreatmentID, PatientID, LabResultTypeID) " _
& "SELECT " _
& Me.TreatmentID & ", " _
& Me.PatientID & ", " _
& "LabResultTypeID FROM LabResultTypes " _
& "WHERE TreatmentTypeID " = & Me.TreatmentTypeID
db.Execute sSQL
MsgBox "Records inserted: " & db.RecordsAffected
Me.Parent.LabResults_Subform.Form.Requery
The query should also include the date and username, as suggested above, if your version is less than 2010 and you have not set up triggers.
You will be tempted to use datasheets, I suspect, for the subforms. I suggest you resist the temptation and use either single forms of continuous forms, they are more powerful. For an interesting approach, you may wish to look at the Northwind sample database Customer Orders form.
Related
The idea of the SaaS tool is to have dynamic tables with dynamic custom fields and values of different types, we were thinking to use "force.com/salesforce.com" example but is seems to be too complicated to maintain moving forward, also making some reports to create with a huge abstraction level, so we came up with simple idea but we have to be sure that this is kinda good approach.
This is the architecture we have today (in few steps).
Each tenant has it own separate database on the cluster (Postgres 12).
TABLE table, used to keep all of those tables as reference, this entity has ManyToOne relation to META table and OneToMany relation with DATA table.
META table is used for metadata configuration, has OneToMany relation with FIELDS (which has name of the fields as well as the type of field e.g. TEXT/INTEGER/BOOLEAN/DATETIME etc. and attribute value - as string, only as reference).
DATA table has ManyToOne relation to TABLES and 50 character varying columns with names like: attribute1...50 which are NULL-able.
Example flow today:
When user wants to open a TABLE DATA e.g. "CARS", we load the META table with all the FIELDS (to get fields for this query). User specified that he want to query against: Brand, Class, Year, Price columns.
We are checking by the logic, the reference for Brand, Class, Year and Price in META>FIELDS table, so we know that Brand = attribute2, Class = attribute 5, Year = attribute6 and Price = attribute7.
We parse his request into a query e.g.: SELECT [attr...2,5,6,7] FROM DATA and then show the results to user, if user decide to do some filters on it, based on this data e.g. Year > 2017 AND Class = 'A' we use CAST() functionality of SQL for example SELECT CAST(attribute6 AS int) AND attribute5 FROM DATA WHERE CAST(attribute6 AS int) > 2017 AND attribute5 = 'A';, so then we can actually support most principles of SQL.
However moving forward we are scared a bit:
Manage such a environment for more tenants while we are going to have more tables (e.g. 50 per customer, with roughly 1-5 mil per TABLE (5mil is maximum which we allow, for bigger data we have BigQuery) which is giving us 50-250 mil rows in single table DATA_X) which might affect performance of the queries, especially when we gave possibilities to manage simple WHERE statements (less,equal,null etc.) using some abstraction language e.g. GET CARS [BRAND,CLASS,PRICE...] FILTER [EQ(CLASS,A),MT(YEAR,2017)] developed to be similar to JQL (Jira Query Language).
Transactions lock, as we allow to batch upload CSV into the DATA_X so once they want to load e.g. 1GB of the data, it kinda locks the table for other systems to access the DATA table.
Keeping multiple NULL columns which can affect space a bit (for now we are not that scared as while TABLE creation, customer can decide how many columns he wants, so based on that we are assigning this TABLE to one of hardcoded entities DATA_5, DATA_10, DATA_15, DATA_20, DATA_30, DATA_50, where numbers corresponds to limitations of the attribute columns, and those entities are different, we also support migration option if they decide to switch from 5 to 10 attributes etc.
We are on super early stage, so we can/should make those before we scale, as we knew that this is most likely not the best approach, but we kept it to run the project for small customers which for now is working just fine.
We were thinking also about JSONB objects but that is not the option, as we want to keep it simple for getting the data.
What do you think about this solution (fyi DATA has PRIMARY key out of 2 tables - (ID,TABLEID) and built in column CreatedAt which is used form most of the queries, so there will be maximum 3 indexes)?
If it seem bad, what would you recommend as the alternative to this solution based on the details which I shared (basically schema-less RDBMS)?
IMHO, I anticipate issues when you wanted to join tables and also using cast etc.
We had followed the approach below that will be of help to you
We have a table called as Cars and also have a couple of tables like CarsMeta, CarsExtension columns. The underlying Cars table will have all the common fields for a ll tenant's. Also, we will have the CarsMeta table point out what are the types of columns that you can have for extending the Cars entity. In the CarsExtension table, you will have columns like StringCol1...5, IntCol1....5, LongCol1...10
In this way, you can easily filter for data also like,
If you have a filter on the base table, perform the search, if results are found, match the ids to the CarsExtension table to get the list of exentended rows for this entity
In case the filter is on the extended fields, do a search on the extension table and match with that of the base entity ids.
As we will have the extension table organized like below
id - UniqueId
entityid - uniqueid (points to the primary key of the entity)
StringCol1 - string,
...
IntCol1 - int,
...
In this case, it will be easy to do a join for entity and then get the data along with the extension fields.
In case you are having the table metadata and data being inferred from separate tables, it will be a difficult task to maintain this over long period of time and also huge volume of data.
HTH
I'm trying to build a database (in PostgreSQL 9.6.6) that allows for one "master column" (items.id) to be replicated in to many (automatically generated) tables (e.g. rank1.id, rank2.id, rank3.id, ...). Only items will have INSERT's (or DELETE's) performed and when they are the newly added id's should also show up (or be removed) in the rankX table(s). To be more concrete:
items:
id | name | description
rank1:
id | rank
rank2:
id | rank
...
Where the id's are always the same, and there is always the same number of rows in each of the tables. The rankX.rank values, however, will be different (imagine users ranking how funny a series of images are -- the images all have the same id's but different users might rank them differently).
What I was thinking was that when a new user was added and a new rankX table created I would do the following:
Have rankX.id referencing a foreign key items.id (with ON DELETE CASCADE)
Copy any items.id that already exist
Auto-generate a trigger function that mirrors the INSERT's to items to the rankX table
This seems cumbersome and wasteful of space since all of the xxxx.id columns are identical and I will end up with hundreds or thousands of trigger functions. As someone new to relational databases I was hoping there was an easier way to achieve this.
So, I have a few questions:
Is there a more efficient way to define my tables such that all of this copying isn't necessary?
If this the best way, can you give an example of how you would set up the triggers (and associated functions)?
Do I need to worry about running out of space on the server as I create (potentially many) sets of triggers of this type?
There are two tables, one for Student and one for Borrowed Books. In the Microsoft Access (2010, 2013), it is easy to display a form Student based on the table Student, and other form BorrowedBooks based on the table Borrowed Books with their Record Source pointed.
How to do Form BorrowedBooks showing records for the current student showing on the Form Student one at one time? I'm looking to learn both VBA script and using the built-in controls to achieve the result.
Relationship between has been built. And it's one student to many books relationship.
One VBA approach like:
DoCmd.OpenForm "BorrowedBooks", , , "StudentID = " & Me.StudentID
The real trick is figuring out which event to put code into.
Another option is to use form/subform arrangement - main form bound to Students table and subform bound to BorrowedBooks. Have you looked at Microsoft Lending Library database template?
so if my understanding of your question is clear then you need to join operation on the tables
SELECT *
FROM
student
INNER JOIN borrowedbooks ON (The two table'S related column ie student.pk=borrowedbooks.fk)
you can also add a where clause if you want from here you can state you primary key in student table and it relationshi
I'm developing an application with SQLAlchemy and PostgreSQL. Users of the system modify data in 8 or so tables. Consider this contrived example schema:
I want to add visible logging to the system to record what has changed, but not necessarily how it has changed. For example: "User A modified product Foo", "User A added user B" or "User C purchased product Bar". So basically I want to store:
Who made the change
A message describing the change
Enough information to reference the object that changed, e.g. the product_id and customer_id when an order is placed, so the user can click through to that entity
I want to show each user a list of recent and relevant changes when they log in to the application (a bit like the main timeline in Facebook etc). And I want to store subscriptions, so that users can subscribe to changes, e.g. "tell me when product X is modified", or "tell me when any products in store S are modified".
I have seen the audit trigger recipe, but I'm not sure it's what I want. That audit trigger might do a good job of recording changes, but how can I quickly filter it to show recent, relevant changes to the user? Options that I'm considering:
Have one column per ID type in the log and subscription tables, with an index on each column
Use full text search, combining the ID types as a tsvector
Use an hstore or json column for the IDs, and index the contents somehow
Store references as URIs (strings) without an index, and walk over the logs in reverse date order, using application logic to filter by URI
Any insights appreciated :)
Edit It seems what I'm talking about it an activity stream. The suggestion in this answer to filter by time first is sounding pretty good.
Since the objects all use uuid for the id field, I think I'll create the activity table like this:
Have a generic reference to the target object, with a uuid column with no foreign key, and an enum column specifying the type of object it refers to.
Have an array column that stores generic uuids (maybe as text[]) of the target object and its parents (e.g. parent categories, store and organisation), and search the array for marching subscriptions. That way a subscription for a parent category can match a child in one step (denormalised).
Put a btree index on the date column, and (maybe) a GIN index on the array UUID column.
I'll probably filter by time first to reduce the amount of searching required. Later, if needed, I'll look at using GIN to index the array column (this partially answers my question "Is there a trick for indexing an hstore in a flexible way?")
Update this is working well. The SQL to fetch a timeline looks something like this:
SELECT *
FROM (
SELECT DISTINCT ON (activity.created, activity.id)
*
FROM activity
LEFT OUTER JOIN unnest(activity.object_ref) WITH ORDINALITY AS act_ref
ON true
LEFT OUTER JOIN subscription
ON subscription.object_id = act_ref.act_ref
WHERE activity.created BETWEEN :lower_date AND :upper_date
AND subscription.user_id = :user_id
ORDER BY activity.created DESC,
activity.id,
act_ref.ordinality DESC
) AS sub
WHERE sub.subscribed = true;
Joining with unnest(...) WITH ORDINALITY, ordering by ordinality, and selecting distinct on the activity ID filters out activities that have been unsubscribed from at a deeper level. If you don't need to do that, then you could avoid the unnest and just use the array containment #> operator, and no subquery:
SELECT *
FROM activity
JOIN subscription ON activity.object_ref #> subscription.object_id
WHERE subscription.user_id = :user_id
AND activity.created BETWEEN :lower_date AND :upper_date
ORDER BY activity.created DESC;
You could also join with the other object tables to get the object titles - but instead, I decided to add a title column to the activity table. This is denormalised, but it doesn't require a complex join with many tables, and it tolerates objects being deleted (which might be the action that triggered the activity logging).
I have created an adaptation of the 'Goods' database that includes a quote feature. The user selects the customer (customer table), Product (product table), qty, discount ect.
The chosen entities then get saved to the quotes table and there is a 'print' function on form.
Whilst the information can be saved and the quote prints via a quote report, I'm having major difficulty in finding a way to add multiple products to a single quote.
The main objective is to be able to select various products and add their total price (product after addition of qty, discount) to a SUB TOTAL
Quote total is therefore the formula Tax+Shipping+SubTotal
any takers? :)
Hi guys,
Thanks for the response I really appreciate it. As for tax and shipping, they are just added in the form and are not pushed from anywhere else in the database. Its simply a type in form and display on report sort of thing. As you said in the answer, HansUp, the salesperson will compute it seperately and just input it.
As for tax, products will be shiped globally so the tax/vat shall be computed seperately also.
Also, each table DOES have its own unique ID.
More to the point of having QuoteProducts. I can't seem to get my head around it! Are you saying that whatever products are chosen in QuoteProducts will create a QuoteProd_ID and then that ID's total price will therefore be added to the Quote?
I tried making a subform before but through the 'multiple records' form but obviously every selection made its own ID. Is there any way you could elaborate on the Quote products part and how it allows multiple records to store to one ID? Without understanding it i'm pretty much useless.
In addition, how the multiple records are then added up to make the subtotal also baffles me. Is that done in the Quote form?
Edit 2
HALLELUJAH.
It works! I created a sum in a textbox on the footer of the subform and then pushed that into subtotal :)
I do have one slight issue:
I made a lookup&relationship for the ListPrice. I don't think its the correct way to do it as it comes up with the price of every light (i.e 10 products priced £10, £10 shows up ten times in dropdown).
Can you guys help?
List Price Problem
here's what i've tried:
1) Create >Client>Query Design
2) Show Products, QuoteDetails. For some reason, it automtically comes up with ListPrice, ProductID (as it should) and Product Name linked to ID in Products
3) Delete links with ListPrice and ProductName.
4) Show all in quoteDetails (*)
5) Create Multiple Items form
Doesnt work! What am I doing wrong?
I'm extremely grateful for both your help. If I can do anything, just shout.
Ryan
In addition to HansUp's stellar answer, you might be interested in DatabaseAnswers.org. They have a number of data models for free that might provide additional insight to your situation and possibly serve as inspiration for future projects you may encounter.
Edit 1
Forget about the form and report for a moment - they are important but not as important as the data and how you store the data.
In your current design, you have a quotes table presumably with an autonumber key field. For the purposes of this answer, this field is named Quote_ID.
The quotes table, as HansUp suggested, should store information such as the Customer_ID, Employee_ID, OrderDate and perhaps even a reference to a BillingAddress and ShippingAddress.
The quotes table SHOULD NOT store anything about the products that the customer has ordered as part of this quote.
Instead, this information should be stored in a table called QuoteProducts or QuoteDetails.
It's structure would look something like the following:
QuoteDetails_ID --> Primary Key of the table (autonumber field)
Quote_ID --> Foreign key back to the Quotes table
Product_ID --> Foreign key back to Products table
Quantity
UnitPrice
You may also want to consider a field for tax and a separate field for shipping per line item on the quote. You will inevitably run into situations where certain items are taxable in some locations and not others, etc.
This design allows a particular quote to have any number of products assigned to the quote.
Returning to your form \ reports, you would need to change your existing forms and reports to accomodate this new table design. Typically one would use a main form for the quote itself, and then a subform for the quote details (item, price, quantity, etc).
To get the quote total, you would sum the items in QUoteDetails for a particular Quote_ID.
You may also want to check out the Northwind sample database from Microsoft. From what I recall Northwind had a sample Orders system that might help make these ideas more concrete for you by seeing a working example.
For the first 3 tables mentioned in your comment, each should have a primary key: Customers, customer_id; products, product_id; and employees, employee_id.
The quotes table will have its own primary key, quote_id, and will store customer_id and employee_id as foreign keys. (I'm assuming you want employee_id to record which customer representative/salesperson created the quote.) You may also decide to include additional attributes for each quote; date and time quote prepared, for example.
The products offered for quotes will be stored in a junction table, QuoteProducts. It will have foreign keys for quote_id and product_id, with one row for each product offered in the quote. This is also where you can store the attributes quantity and discount. An additional field, unit_price, can allow you to store the product price which was effective at the time the quote was prepared ... which would be useful in case product prices change over time. I don't know whether tax should be included in this table (see below).
I also don't know how to address shipping. If all the products associated with a quote are intended to be delivered in one shipment, shipping cost could be an attribute of the quotes table. I don't know how you intend to derive that value. Seems like it might be determined by shipping method, distance, and weight. If you have the salesperson compute that value separately, and then input the value, consider how to handle the case where the product selection changes after the shipping fee has been entered.
That design is somewhat simplistic, but might be sufficient for the situation you described. However, it could get more complex. For example, if you decide to maintain a history of product price changes, you would be better off to build in provisions for that now. Also, I have no idea how tax applies in your situation --- whether it's a single rate applied to all products, varies by customer location, varies by type of customer, and/or varies by product. Your business rules for taxes will need to be accommodated in the schema design.
However, if that design works for you (test it by entering dummy data into the tables without using a form), you could create a form based on quotes with a subform based on QuoteProducts. With quote_id as the link master/child property, the subform will allow you to view all products associated with the main form's current quote_id. You can use the subform to add, remove, and/or edit products associated with that quote.
Not much I can say about the report. There is a lot of uncertainty in the preceding description. However, if your data base design allows you to build a workable form/subform, it should also support a query which gathers the same data. Use that query as the record source for the report. And use the report's sorting and grouping features to create the quote grand total.
Edit: With the main form/ subform approach, each new row in the subform should "inherit" the quote_id value of the current record in the main form. You ensure that happens by setting the link master/child properties to quote_id. Crystal Long explains that in more detail in chapter 5 of Access Basics by Crystal: PDF file. Scroll down to the heading Creating a Main Form and Subform on page 24.
Edit2: Your strategy may include storing Products.ListPrice in QuoteDetails.ListPrice. That would be useful to record the current ListPrice offered for a quote. If so, you can fetch ListPrice from Products and store it in QuoteDetails when you select the ProductID for a row in the subform. You can do that with VBA code in the after update event of the control which is bound to the ProductID field. So if that control is a combo box named cboProductID and the subform control bound to the QuoteDetails ListPrice field is a text box named txtListPrice, use code like this for cboProductID after update:
Me.txtListPrice = DLookup("ListPrice", "Products", "ProductID = " _
& Me.cboProductID)
That suggestion assumes the Products and QuoteDetails tables both include a ProductID field and its data type is numeric. And cboProductID has ProductID as its bound field and uses a query as its RowSource similar to this:
SELECT ProductID, ProductName
FROM Products
ORDER BY ProductName;