Enforce Unique on multiple sets of same data? - mysql

What i want to do is this:
a, b - valid.
a, b - valid.
a, c - invalid
f, b - invalid.
So this means the database will allow the same combination of values to exists multiple times but will not allow any other combo to use those values. So it is like a Unique on a combination set. My usecase is i have a group_id and group_name in the table. I dont want peple to mess up the group_name for an ID. So if the first ID of 2 has group_name as 'apple' then all IDs with 2 must be 'apple' and should throw error if 'banana' is entered.
One way is i use a 2nd table and normalize the data but i cant do that here, need to use it denormalized. So is there anyway to enforce this check?
Thanks

maybe validate during insert and update trigger.

Related

SQL - Is there a LIKE / Contains / Wildcard function when I don't have a specific criteria?

EG:
Table A has a Name column
Table B has a Name column
I have used the below code for an exact match
SELECT Table_A.Name, Table_B.Name
FROM Table A, Table B
WHERE Table_A.Name = Table_B.Name;
Now I want to check if there are any columns that are similar.
I tried this.
SELECT Table_A.Name, Table_B.Name
FROM Table A, Table B
WHERE Table_A.Name LIKE '%Table_B.Name%';
I want to be able to pull up names in Table A that are similar to Table B. It's my understanding that the LIKE operator only works if you know what criteria you are looking for. So obviously the above statement doesn't work.
Is this possible or am I way off the mark?
This syntax would work, but it is not exactly 'similar names'; it is if the name Table_B appears somewhere in Table_A.name:
SELECT Table_A.Name, Table_B.Name
FROM Table A, Table B
WHERE Table_A.Name LIKE '*' & Table_B.Name & '*';
Also the wildcard characters in MsAccess are not standard SQL, so they are * and ?, replacing % and _, respectively.
For the scenario: Table A: Ted Smith Table B: Teddy Smith:
You can't use the LIKE operator to capture such senarios unless you also maintain a name-similarities table (and mark Ted as Teddy). Approximate name matching is a vast subject, there are algorithms for it like SOUNDEX and METAPHONE (search for them, there are VBA implementations you can use.) UPDATE:20220401: Forgot to add EDIT DISTANCE.
Here is one:http://www.allenbrowne.com/vba-Soundex.html
If you adopt a Soundex function you can use it like:
SELECT Table_A.Name, Table_B.Name
FROM Table A, Table B
WHERE SoundEx([NameA])=SoundEx([NameB])`.
Beware though; for large data-sets this will be slow!
I also sugegst that you read about the strengths/weaknesses of the algorithm, especially when used with names where first and lastname are combined. These algorithms are generally used in combination with other methods (like LIKE, and or other relevant information about the person, like address), and with user assistance (ie the possible matches are presented to an operator allowing her/him to choose).

Is there way to add multiple values to 1 ID in access

I have a table that has Act ID, and another table that has Act ID, percentage complete. This can have multiple entries for different days. I need the sum of the percentage added for the Act ID on the first tableZA.108381.080
First table
Act ID Percent Date
ZA.108381.110 Total from 2 table
ZA.108381.120
ZA.108476.020
ZA.108381.110 25% 5/25/19
ZA.108381.110 75 6/1/19
ZA.108381.120
ZA.108476.020
This would be generally considering not good practice. Your primary key should be uniquely identifiable for that specific table, and any other data related to that key should be stored in separate columns.
However since an answer is not a place for a lecture, if you want to store multiple values in you Act ID column, I would suggest changing your primary key to something more generic "RowID". Then using vba to insert multiple values into this field.
However changing the primary key late in a databases life may cause alot of issues or be difficult. So good luck
Storing calculated values in a table has the disadvantage that these entries may become outdated as the other table is updated. It is preferable to query the tables on the fly to always get uptodate results
SELECT A.ActID, SUM(B.Percentage) AS SumPercent
FROM
table1 A
LEFT JOIN table2 B
ON A.ActID = B.ActID
GROUP BY A.ActID
ORDER BY A.ActID
This query allows you to add additional columns from the first table. If you only need the ActID from table 1, then you can simplify the query, and instead take it from table 2:
SELECT ActID, SUM(Percentage) AS SumPercent
FROM table2
GROUP BY ActID
ORDER BY ActID
If you have spaces other other special characters in a column or table name, you must escape it with []. E.g. [Act ID].
Do not change the IDs in the table. If you want to have the result displayed as the ID merged with the sum, change the query to
SELECT A.ActID & "." & Format(SUM(B.Percentage), "0.000") AS Result
FROM ...
See also: SQL GROUP BY Statement (w3schools)

sql: selecting a value in an attributes table

I have a database with the following schema:
thing
id
id_thing_type
thing_attribute
id
name
id_thing_type
thing_attribute_value
id_thing_attribute
value_date
Thing has many Thing_Attributes joined on thing.id_thing_type = thing_attribute.id_thing_type
Thing_Attributes have one Thing_Attribute_Value joined on thing_attribute.id = thing_attribute_value.id_thing_attribute
I am trying to write a query that will return the value of a specific attribute (with a certain name) for a specific thing record (with a certain id). thing.id represents a unique row.
Said another way, thing_attribute and thing both have an id_thing_type. These tables need to be joined on id_thing_type = id_thing_type. Think of this as each type of thing has its own unique set of thing_attributes. The task is to find the value of a specific attribute of a specific thing.
This is what I have so far, however it returns many rows:
SELECT t.id, tav.value_date
FROM thing_attribute_value tav
JOIN thing_attribute ta
ON tav.id_node_attribute = ta.id
JOIN thing t
ON ta.id_thing_type = t.id_thing_type
WHERE ta.name = 'Birth Date'
AND t.id = '123'
Here is an example result. As you can see, many rows are returned, all with the same id for thing, but with different dates.
123,2015-12-02
123,2014-11-02
123,2013-07-11
123,2014-03-12
etc....
So, as discussed in the comments to my original answer:
Thing has a Thing_Type_Id Column.
And Thing_Attribute has a Thing_Type_Id Column.
For which there is a 1::1 relationship.
Thing_Attribute has a Thing_Attribute_Value_Id column.
And Thing_Attribute_Value has an Id column.
For which there is a 1::Many relationship.
This is what is causing your query to fail. The error is actually in your data relationships, not the query.
Let's try to piece this together:
Thing
- needs a unique id
- can have any number of other columns
- will have many attributes, but this column will not be in this table
Thing_Attributes
- needs a unique id
- needs a Thing id (fk to Thing)
- needs a Type id (fk to Thing_Attribute_Type)
- needs a Value
Thing_Attribute_Value - can go away - Replace it with a lookup for Type
Thing_Attribute_Type
- needs unique id
- needs Value
With this relational structure you can have many Attributes for each Thing
You use an id field for Attribute Type so you are not repeating a strings.
Use a lookup that gets an Attribute_Type value based on the Id.
This is an overhaul to your current relationship model, but with your current one you cannot isolate a given attribute value to a given Thing, which I think was at least one of the goals in building your Thing objects.

Query to return rows that are associated with many values in a specific column

I have a relationship table with an unique ID and two columns a and b, each being foreign keys to two entity tables A and B. The relationship is many to many between A and B, so the way the relationship table works now is that there are many rows that have an element of table A with different elements of table B, such as:
<1>, <A1>, <B1>
<2>, <A1>, <B5>
<3>, <A1>, <B3>
<4>, <A1>, <B150>
<5>, <A1>, <B12>
<6>, <A2>, <B72>
. . .
Now I need a query that gives me the elements of type A that have corresponding rows with all the values in a set. So SELECT a FROM table WHERE b IN (B3, B17, ..., B178) is not what I want).
The reason is that this simple query returns the rows that have either of the values, but as in my question, I want the A entities that have rows with ALL the values of b in the input set (B3, B17, ..., B178).
In other words only the rows for which GROUP_CONCAT of column b when GROUP BY a is equal to the input set.
Also maybe it is good to mention that this input set starts with 2 values but will grow in each phase of my algorithm so in worst case it might have all the possible values for column b (although the result of this specific query for the worst case is most probably null).
Use group by and having:
SELECT a
FROM table
WHERE b IN (B3, B17, ..., B178)
GROUP BY a
HAVING COUNT(*) = <number of elements in the list>;
If your table can have duplicates, then you want COUNT(DISTINCT b).

Multiple "where" -s from one table into one view

I have a table called "users" with 4 fields: ID, UNAME, NAME, SHOW_NAME.
I wish to put this data into one view so that if SHOW_NAME is not set, "UNAME" should be selected as "NAME", otherwise "NAME".
My current query:
SELECT id AS id, uname AS name
FROM users
WHERE show_name != 1
UNION
SELECT id AS id, name AS name
FROM users
WHERE show_name = 1
This generally works, but it does seem to lose the primary key (NaviCat telling me "users_view does not have a primary key...") - which I think is bad.
Is there a better way?
That should be fine. I'm not sure why it's complaining about the loss of a primary key.
I will offer one piece of advice. When you know that there can be no duplicates in your union (such as the two parts being when x = 1 and when x != 1), you should use union all.
The union clause will attempt to remove duplicates which, in this case, is a waste of time.
If you want more targeted assistance, it's probably best if you post the details of the view and the underlying table. Views themselves don't tend to have primary keys or indexes, relying instead on the underlying tables.
So this may well be a problem with your "NaviCat" product (whatever that is) expecting to see a primary key (in other words, it's not built very well for views).
If i am understanding your question correctly, you should be able to just use a CASE statement like below for your logic
SELECT
CASE WHEN SHOW_NAME ==1 THEN NAME ELSE UNAME END
FROM users
This can likely be better written as the following:
SELECT id AS id, IF(show_name == 1, name, uname) AS name
FROM users