So I created a database table in MySQL that held permission rights for permissions and commands, the command rights started with the prefix command_ in the column permission_name and then I have an extra column called allowed_ranks, which is a list of INT rank ID's that are required, split by a , character.
The issue is, the command ones were anything higher, and I've put 1 id in allowed_ranks, is there a way I can loop through all the ones with column starting with command_ and change the allowed_ranks that are just 1 ID to every number starting from that to 9? 9 is the maximum rank ID.
I've already done part of the query, I'm just not sure how to do the updating?
UPDATE permission_rights` SET `allowed_ranks` = '?' WHERE `permission_name` LIKE 'command_%';
How would I update it to every number after the columns value up to 9? So lets say I had this record... just a quick example to ensure you know what I mean.
| permission_name | allowed_ids |
----------------------------------
| command_hello | 2
| command_junk | 5
| command_delete | 8
| command_update | 1
Would become...
| permission_name | allowed_ids |
----------------------------------
| command_hello | 2,3,4,5,6,7,8,9
| command_junk | 5,6,7,8,9
| command_delete | 8,9
| command_update | 1,2,3,4,5,6,7,8,9
The better approach would be to use a number generator (some method which will produce number from 1 to n), but general MySQL has no such capability.
If you use MariaDB you can use seq_1_to_1000 as suggested here in Answer by O.Jones.
However your use case seems to be simpler, since you said that the highest rank is 9, I would just use
update a
set a.allowed_ids = RIGHT('1,2,3,4,5,6,7,8,9',19-2*a.allowed_ids)
where a.permission_name like 'command_%'
Related
I have a Table (T_agents) of agents each has a number of call in a field called NCH I want to create another field call NCHpercent that is the percentage of calls taken by that agent. So the formula is NCH/Total NCH.
So in the query builder I have the following and formula but it dosent work :(
NCHpercent: [NCH.T_agents] / ( SUM(SELECT [NCH.T_agents] FROM [T_agents]) )
What am I doing wrong ?
This would be easier if we could see the table structure as that impacts everything. However I hope I follow this correctly, but I imagine your table (T_agents) as something like:
+-------+-------------+------+
| ID | Agents | NCH |
+-------+-------------+------+
| 1 | agent_1 | 1 |
| 1 | agent_1 | 1 |
| 1 | agent_2 | 2 |
| 1 | agent_3 | 1 |
+-------+-------------+------+
Now assuming that is correct (and NCH is not a unique ID but a total number of calls then we can use a query like this to calculate percentage - note this is not stored in a table, this is just to display the percentage value in a query- I've also added the sum of the total in for the sake of it:
SELECT SUM([T_Agents].NCH) AS total_SUM, [T_agents].Agents, ((SUM(T_agents.NCH))/(select SUM(t_agents.NCH )from T_agents)*100) AS NCHPercent
FROM T_agents
GROUP BY [t_agents].Agents;
In my test the results would be:
2, agent_1, 40
2, agent_2, 40
1, agent_3, 20
However if I got this wrong and the NCH column is in fact
Ok. I just found the answer soing some trial an error. The answer is this code:
NCHperc: [AHT_Tenure].[Calls Handled]/(SELECT Sum(AHT_Tenure.[Calls Handled]) AS [SumaDeCalls Handled]
FROM AHT_Tenure)
By the way thank you guys. And actually the agents name dosent matter for this query since all I wanted was the percentage on each row.
I have put a lot of effort into my database design, but I think I am
now realizing I made a major mistake.
Background: (Skip to 'Problem' if you don't need background.)
The DB supports a custom CMS layer for a website template. Users of the
template are limited to turning pages on and off, but not creating
their own 'new' pages. Further, many elements are non editable.
Therefore, if a page has a piece of text I want them to be able to edit,
I would have 'manually' assigned a static ID to it:
<h2><%= CMS.getDataItemByID(123456) %></h2>
Note: The scripting language is not relevant to this question, but the design forces
each table to have unique column names. Hence the convention of 'TableNameSingular_id'
for the primary key etc.
The scripting language would do a lookup on these tables to find the string.
mysql> SELECT * FROM CMSData WHERE CMSData_data_id = 123456;
+------------+-----------------+-----------------------------+
| CMSData_id | CMSData_data_id | CMSData_CMSDataType_type_id |
+------------+-----------------+-----------------------------+
| 1 | 123456 | 1 |
+------------+-----------------+-----------------------------+
mysql> SELECT * FROM CMSDataTypes WHERE CMSDataType_type_id = 1;
+----------------+---------------------+-----------------------+------------------------+
| CMSDataType_id | CMSDataType_type_id | CMSDataType_type_name | CMSDataType_table_name |
+----------------+---------------------+-----------------------+------------------------+
| 1 | 1 | String | CMSStrings |
+----------------+---------------------+-----------------------+------------------------+
mysql> SELECT * FROM CMSStrings WHERE CMSString_CMSData_data_id=123456;
+--------------+---------------------------+----------------------------------+
| CMSString_id | CMSString_CMSData_data_id | CMSString_string |
+--------------+--------------------------------------------------------------+
| 1 | 123456 | The answer to the universe is 42.|
+--------------+---------------------------+----------------------------------+
The rendered text would then be:
<h2>The answer to the universe is 42.</h2>
This works great for 'static' elements, such as the example above. I used the exact same
method for other data types such as file specifications, EMail Addresses, Dates, etc.
However, it fails for when I want to allow the User to dynamically generate content.
For example, there is an 'Events' page and they will be dynamically created by the
User by clicking 'Add Event' or 'Delete Event'.
An Event table will use keys to reference other tables with the following data items:
Data Item: Table:
--------------------------------------------------
Date CMSDates
Title CMSStrings (As show above)
Description CMSTexts (MySQL TEXT data type.)
--------------------------------------------------
Problem:
That means, each time an Event is created, I need to create the
following rows in the CMSData table;
+------------+-----------------+-----------------------------+
| CMSData_id | CMSData_data_id | CMSData_CMSDataType_type_id |
+------------+-----------------+-----------------------------+
| x | y | 6 | (Event)
| x+1 | y+1 | 5 | (Date)
| x+2 | y+2 | 1 | (Title)
| x+3 | y+3 | 3 | (Description)
+------------+-----------------+-----------------------------+
But, there is the problem. In MySQL, you can have only 1 AUTO INCREMENT field.
If I query for the highest value of CMSData_data_id and just add 1 to it, there
is a chance there is a race condition, and someone else grabs it first.
How is this issue typically resolved - or avoided in the first place?
Thanks,
Eric
The id should be meaningless, except to be unique. Your design should work no matter if the block of 4 ids is contiguous or not.
Redesign your implementation to add the parts separately, not as a block of 4. Doing so should simplify things overall, and improve your scalability.
What about locking the table before writing into it? This way, when you are inserting a row in the CMSData table, you can get the last id.
Other suggestion would be to not have an incremented id, but a unique generated one, like a guid or so.
Lock Tables
Is it possible to add a database constraint to limit a row to have a single value in one of two columns, never more and never less? Let me illustrate:
Sales Order Table
---------------------------------
id | person_id | company_id |
Rows for this would look like:
id | person_id | company_id |
---|-----------|------------|
1 | 1 | null |
2 | 2 | null |
3 | null | 1 |
4 | null | 2 |
In this illustration, the source of the sales order is either a person or a company. It is one or the other, no more or less. My question is: is there a way to constrain the database so that 1) both fields can't be null and 2) both fields can't be not-null? i.e., one has to be null and one has to be not-null...
I know the initial reaction from some may be to combine the two tables (person, company) into one customer table. But, the example I'm giving is just a very simple example. In my application the two fields I'm working with cannot be combined into one.
The DBMS I'm working with is MySQL.
I hope the question makes sense. Thank you in advance for your help!
This may come as a shock...
mysql doesn't support CHECKconstraints. It allows you to define them, but it totally ignores them.
They are allowed in the syntax only to provide compatibility with other database's syntax.
You could use a trigger on update/insert, and use SIGNAL to raise an exception.
Update: Question refined, I still need help!
I have the following table structure:
table reports:
ID | time | title | (extra columns)
1 | 1364762762 | xxx | ...
Multiple object tables that have the following structure
ID | objectID | time | title | (extra columns)
1 | 1 | 1222222222 | ... | ...
2 | 2 | 1333333333 | ... | ...
3 | 3 | 1444444444 | ... | ...
4 | 1 | 1555555555 | ... | ...
In the object tables, on an object update a new version with the same objectID is inserted, so that the old versions are still available. For example see the entries with objectID = 1
In the reports table, a report is inserted but never updated/edited.
What I want to be able to do is the following:
For each entry in my reports table, I want to be able to query the state of all objects, like they were, when the report was created.
For example lets look at the sample report above with ID 1. At the time it was created (see the time column), the current version of objectID 1 was the entry with ID 1 (entry ID 4 did not exist at that point).
ObjectID 2 also existed with it's current version with entry ID 2.
I am not sure how to achieve this.
I could use a query that selects the object versions by the time column:
SELECT *
FROM (
SELECT *
FROM objects
WHERE time < [reportTime]
ORDER BY time DESC
)
GROUP BY objectID
Lets not talk about the performance of this query, it is just to make clear what I want to do. My problem is the comparison of the time columns. I think this is no good way to make sure that I got the right object versions, because the system time may change "for any reason" and the time column would then have wrong data in it, which would lead to wrong results.
What would be another way to do so?
I thought about not using a time column for this, but instead a GLOBAL incremental value that I know the insertion order across the database tables.
If you are interting new versions of the object, and your problem is the time column(I assume you are using this column to sort which one is newer); I suggest you to use an auto-incremental ID column for the versions. Eventually, even if the time value is not reliable for you, the ID will be.Since it is always increasing. So higher ID, newer version.
I'm creating a database with various tables. Let's take the user table, for example. It has fields such as marital status and system role. Each of those fields has predefined options. Does it make sense to create two new tables for each of those fields, so then when a user is added to the system, choices can be made available for selection e.g. single, married, divorced? It seems a bit of an overkill in terms of one extra query. Is this the best way to do it or do I have other options?
I would definitely create separate tables to store the available options for these various columns. This is a good thing to do as far as normalization goes, and will also save you headaches down the road when you need to add, remove, disable or change any of the options. Also, if don't create a separate table and populate the values directly in the user table, you may end up having to do something like select distinct RelationshipStatus from User to get the available options, which is not as performant as just selecting 10 or however many values from a separate table.
As someone commented, over-normalization can sometimes be a pain, but I've found that not normalizing something as a way to do a quick work-around almost always comes back to haunt you.
User
----
ID
RelationshipStatusId
...other columns
RelationshipStatus
------------------
ID
Value
Description
You can use the ENUM datatype in MySQL to better take care of this scenario. Storing such options in a seperate table is a bad idea until you have a lot of them..
mysql> DESC Classes;
+-------+-----------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-----------------------+------+-----+---------+-------+
| id | int(11) | NO | PRI | NULL | |
| dept | char(4) | NO | | NULL | |
| level | enum('Upper','Lower') | NO | | NULL | |
+-------+-----------------------+------+-----+---------+-------+
3 rows in set (0.00 sec)
mysql> SELECT * FROM Classes;
+----+------+-------+
| id | dept | level |
+----+------+-------+
| 10 | MATH | |
+----+------+-------+
1 row in set (0.00 sec)
mysql> INSERT INTO Classes VALUES (11, 'ENG', 'Upper')
-> ;
Query OK, 1 row affected (0.00 sec)
mysql> SELECT * FROM Classes;
+----+------+-------+
| id | dept | level |
+----+------+-------+
| 10 | MATH | |
| 11 | ENG | Upper |
+----+------+-------+
2 rows in set (0.00 sec)
For design's sake, create another table (what you don't want to do) with a proper PK. This will have the extra benefit of saving space, because imagine having 10000 registers with the word "married" on them.
Also, an alternative is using in your application a "dictionary", storing in a structure and Id and the value, like this:
Id Marital Status
1 Married
2 Single
.. ......
The same table, but not in a database but in the application, hardcoded, serialized or in an external file.
It depends on the size of the rows also. It would be better option to split the tables in to multiple in terms of speed.
For ex. you can keep the frequent used columns in user table and all other informations/optional ones in separate tables. In this case you need take care while displaying the data also.
I guess, there is no need for over-normalization as it will trouble you in writing queries. You need to take care of too many joins.
If your predefined conditions for Marital Status are: Married, Single and Divorced, I would just store a single character like: M, S and D and would provide these options in a DropDown with fixed values.
I think Marital Status has no further possibilities unless you think of something like:
Want to be Divorced
Married but living alone.
For user role also, I would do something like that:
A - Administrator
P - Power User
R - Restricted User
G - Guest
In case you need something more elaborate, I won't create further tables.