web2py prevent duplicates in many to many table - duplicates

I have a table to manage many to many relationship (workers and skills)
Workers can have multiple skills and one skill can be assigned to multiple workers
What would be the best way to prevent duplicate entries so one skill is not assigned to the same worker twice?
thank you

If you have something like:
db.define_table('worker_skill',
Field('worker', 'reference worker'),
Field('skill', 'reference skill'))
To prevent duplicates via form submissions, you could then add a validator to one of the fields, such as:
db.worker_skill.skill.requires = IS_NOT_IN_DB(
db(db.worker_skill.worker == request.vars.worker), 'worker_skill.skill'
)
The above will ensure that the value being inserted in "skill" does not exist among the set of records where the value being inserted in "worker" matches the "worker" field.
Another option for form validation is use of an onvalidation callback, as explained in the forms chapter of the book.
You can also set a unique constraint on the pair of columns directly in the database (web2py does not handle that, so you will have to do that via an external tool). That will not help with form validation, as a violation of the constraint will simply result in the database driver throwing an exception (rather than presenting a friendly error message to the end user), but it will be useful if you are making inserts via means other than a web2py form.

Related

Mysql: possible to add constraint that prevents a one to many relation from having less than certain number of relations?

I have a user table that has many, say user_property table, where the foreign user_id is stored in the user_property table.
Now is it possible to add constraint so that a user should have at least one user property? So when a user have five properties, he can delete it one by one, but when there is only one property left, he can not delete it? I tried Googling but I am not even sure what is the search keyword for this.
The reason is, I want to avoid checking if a user have one property remaining only from the application layer, because it reads from replica, the read and write might not be synchronized, and on certain condition the user might accidentally delete all properties.
Any suggestion or different approaches is appreciated.
I don't think you can do this with a constraint. The problem is handling new users. You cannot insert a new user, because it has no properties. You cannot insert a new property, because the user reference is not valid. Ouch!
One solution involves triggers. The idea is the following:
Add to the the users table a column for the number of current properties.
Add to the users table a column for the maximum number of properties ever.
Default the two values to 0 for new users.
Add a check constraint (or trigger) that when the maximum is > 0 then the current number has to be > 0.
In any database, you need to implement the first two counts using triggers (on user_property). MySQL does not support check constraints, so the last condition also requires a trigger.
There is no constraint in SQL that does what you describe.
A foreign key constraint would ensure that every row in user_property must reference a row that exists in the user table.
But there is no constraint in SQL that does the reverse: ensure every user is referenced by at least one row in user_property.
A CHECK constraint has been mentioned by some other comments and answers. But a CHECK constraint can reference only columns of the same row. It can't reference other rows of the same table or different tables.
The most straightforward solution is to handle this in application code. That is:
Implement a function that INSERTs to user, while making sure there's also an INSERT of the first row to user_property.
Implement a function that DELETEs from user_property, but first check if it's would leave zero properties for the given user_id. If so, return an error instead of deleting the user property.
Implementing such data integrity rules in application code comes with a risk, of course. What if you have multiple apps that access the same database? You need to implement the same rules in different apps. Perhaps even in different programming languages. Sounds like a PITA.
Nevertheless, not all business rules can be implemented with simple SQL declarative constraints.

MS Access 2013 query query criteria can't assess if value A is contained in value B string

Issue:
I am developing a simple issue tracking database and have hit a stumbling block that I’m not sure how to resolve. Have tried several approaches using queries, sql statement etc but still not working. I may have to rethink how I am doing this but hoping someone may be able to address the issue as it stands, though if a more elegant way of doing it happy to implement that.
Scenario:
A table called tblUsers has a field called Access that is a lookup to a table called tblCategory and allows for multiple values to be stored (one to many). In essence this is saying which category(s) of “issue” the user is allowed to
A simple msgbox test in code shows that this is correctly storing the values selected in the following format "1, 2, 3, 4"
In turn, each issue can only have a single category (one to one) which is stored in a field called Category in table tblGMPIssues and is also populated from a lookup to the tblCategory table.
So far so good ….
I then have a query called qryUserIssues that should show all issues from the table tblGMPIssues that are a) “Open” (status = 1) and that b) match any of the categories that the user is permitted to view.
I can get this to work with a single value i.e. as it stands query prompts for input and if you enter a single valid integer it returns expected results
But I can’t work out the syntax to get the criteria to accommodate multiple values. For example, in above scenario our user should be allowed to see 4 different category or calls “1, 2, 3, 4”
Tried using INNER joins, tried assigning to variables and using a LIKE criteria but can’t seem to get the syntax right.
If anyone could let me know if this can be done and if so how as it’s driving me nuts.
All help and suggestions gratefully received.
Updated relationship diagram --> 1
For precisely the reason that you've asked this question I would recommend never using the multi-select lookup option for columns in MS Access tables. Instead create an intersection table which tells you the combinations of values from the two main tables that are allowed. So instead of having the multi-select Access column in tblUsers, you should have a separate table called tblUserAccess with two columns (UserID and CategoryID). The two columns together will form a composite Primary Key for this table, and individually they will be Foreign Keys to tblUsers and tblCategory respectively. (You should do the same kind of thing with tblType - remove the Categories column and set up a separate table called tblTypeCategories).
Coming to your query, are you expecting this to show you all the relevant Issues for a particular user? At the moment, it is not doing this. The reason it is prompting you for input is because it doesn't understand ([tblUsers].[Access]) - tblUsers is not referenced in your query, and the query has no way of knowing which particular user you're interested in.
With your new table in place (and populated with the relevant data) you should add tblUserAccess to the query, joining tblGMPIssues.Category to tblUserAccess.CategoryID. Take the ([tblUsers].[Access]) condition off the Category column. Add the UserID column to the grid and set the criteria to [Input UserID]. Now when you run the query it will ask you for a user ID, and it should hopefully show you all the Issues that the given user can access.
Good luck!
First, I suggest you normalize your data a bit:
You have a number of tables that are reference data (e.g. tables tblStatus, tblSeverity, tblLocation). You have a s a primary key a (system generated) ID. That is wrong! The primary key of these should be their data, i.e. status, severity, location.
I can't see what the relationships are between the data. It should be one-to-many, mandatory (i.e. one Status can occur in many tblGMPIssues and a status is mandatory).
Your table tblType is unclear to me but it contains the categories. I am not familiar with the '-' before Categories followed by a Categories.Value but I assume an occurrence of tblType can contain exactly one Categories.Value. If not, then you must decompose this table.
If a User has access to a number of Categories, then there must be a many-to-many relationship betwen Users and Categories. From this relationship you do your select query, but I don't see this relationship.
Use following query to get any of the Category IDs 1, 2, 3 or 4
Select * from tblGMPIssues where tblGMPIssues.Category in (Select UserAccess from tblUserAccess)
I still have many problems with your relational design, or actually the lack of a proper relational design. As an example, below is a diagram from my Access 2007 showing a part of your database with a proper design. Access automatically shows that "one" and "many" symbols (which I don't see in your diagrams). I also show the relationship dialog with the proper fields checked. Note that none of the keys of any table, except tblIssue, has a system generated primary key. They are all plain text whch allows better understanding when inspecting the data and, as said, the database automaticlly updates child tables when the primary key value of a parent table changes.
Note table tblCategoryType: it implements a many-to-many relation between categories and types, meaning a category can be of zero or more types and a type can be in zero or more categories. In addition to "update cascades", this table has the "delete cascades" checkbox checked so if a category is deleted, all its relations with types are deleted (not the types).

How to get Access to stop enforcing a one-to-many relationship

I have a form containing a bunch of time sheet entries. Now most of these entries are related to some kind of "job" we have running through the shop, but not always. When there is, you just pop the job ID into the corresponding time sheet entry and a lot of relevant information about the job is displayed - which is necessary.
However, my problem occurs when a time sheet entry DOES NOT relate to a specific job. I still want to use the same form and mechanisms to input the entry, but if the entry isn't supplied with a Job ID, Access tells me that it can't find a record in the Job table that corresponds with the entry. It will not let me save the record at that point.
So Jobs have a one-to-many relationship with the timesheet entries, but only when a relationship exists at all. How do I make Access not freak out when a Job ID isn't supplied? I've already checked the relationship and it is not enforcing referential integrity. Also the Job ID field is not marked as required in the time sheet entries table.
Can you add a default value like "Unspecified" or "not billable" to the jobs list? Then you can maintain your referential integrity and you don't have to guess whether or not a NULL job IDis a missing real job ID or or one of these non-billable items.

Storing Email Unsubscriptions to DB

I'm working on a product that sends periodic emails to the registered customers, and I would like to implement some kind of an unsubscription mechanism from these emails.
There are about 5 types of emails and a User MySql table that contains all the users. New users are subscribed to all the email types by default, and can unsubscribe from each email type separately.
My question is how should I store these unsubscriptions to the DB, while keeping high performance and scalability and without over-complicating things. Here are a few options that came up, each has its own advantages and disadvantages:
Adding a boolean column on the User table for every report type with a default value of true.
Creating a new Unsubscription table with one-to-one relationship to the User table. Every email type will get a column and every user get a row.
Creating a new Unsubscription table with many-to-one relationship to the User table. Every unsubscription request will create a new row on the table.
Is there a best practice for storing unsubscription information? What are the database-design issues?
option 3. is the most 'normalised' in terms of db schema and means email types can be added without having to do any migrations on the db... it's also the most natural option if you already have a table for storing email types
however you will get better performance with option 1. (no JOINs) at the expense of needing to do a db migration if you add a new email type
option 2. seems to have the inflexibility of 1. while still needing a separate table so would be my least favoured option
A couple of other options to consider:
instead of several boolean fields on the model (option 1.) use a single BitField https://github.com/disqus/django-bitfield to represent unsubscriptions... this has the advantage that you can add new email types without migrating, plus querying is just as fast. removing types you'd have to be careful though
as mentioned above, if you have a table for EmailType already it makes sense to have a many-to-many relation on the User model. But you could use django-denorm to automatically update a BitField on the model which might give the best of both worlds

Mysql Constraign Database Entries in Rails

I am using Mysql 5 and Ruby On Rails 2.3.1. I have some validations that occasionally fail to prevent duplicate items being saved to my database. Is it possible at the database level to restrict a duplicate entry to be created based on certain parameters?
I am saving emails to a database, and don't want to save a duplicate subject line, body, and sender address. Does anyone know how to impose such a limit on a DB through a migration?
You have a number of options to ensure a unique value set is inserted into your table. Lets consider 1) Push responsibility to the database engine or 2) your application's responsibilitiy.
Pushing responsibility to the database engine could entail the use of creating a UNIQUE index on your table. See MySql Create Index syntax. Note, this solution may result in an exception thrown in case a duplicate value is inserted. As you've identified what I infer to be three columns to determine uniqueness (subject line, body, and sender address) you'll create the index to include all three columns. Its been a while since I've worked with Rails so you may want to check the record count inserted as well.
If you desire to push this responsibility to your application software you'll need to contend with potential data insertion conflicts. That is, assume you have two users creating an email simultaneously (just work with me here) having the same subject line, body, and send address. Should your code simple query for any records consisting of the text (identical for both users in this example) both will return no records found and will proceed along merily inserting their emails which now violate your premise. So, you can address this with perhaps a table lock, or some other syncing field in the database to ensure duplicates don't appear. This latter approach could consist of another table with a single field indicating if someone is inserting a record or not, once completed it updates that record to state it has completed and then others can proceed.
While there you can have a separate architectural discussion on the implications of each alternative I'll leave that to a separate post. Hopefully this suffices in answering your question.
You should be able to add a unique index to any columns you want to be unique throughout the table.