MySQL choose one field in duplicated values - mysql

I have a table of users rights. These rights can be Write or Read. I have a SQL view which merges profile_user table and right_user table. This view give me the following results:
+-------+-----------+---------------+
| email | right | write_or_read |
+-------+-----------+---------------+
| admin | dashboard | write |
+-------+-----------+---------------+
| admin | dashboard | read |
+-------+-----------+---------------+
| admin | log | read |
+-------+-----------+---------------+
How can I un-duplicate dashboard write to get only the most important (the write right).
I want to write an SQL query which gives me the following result:
+-------+-----------+---------------+
| admin | dashboard | write |
+-------+-----------+---------------+
| admin | log | read |
+-------+-----------+---------------+
I read this question but the answer covers only id numeric field to get the right record.

In this case, a simple max() works:
select email, right, max(write_or_read)
from user_rights
group by email, right;

You can use CASE expression to list the conditions in priority. This works for more than two values as well:
SELECT email, right, CASE
WHEN SUM(write_or_read = 'write') > 0 THEN 'write'
WHEN SUM(write_or_read = 'read') > 0 THEN 'read'
-- more conditions
END AS permission
FROM t
GROUP BY email, right

Related

Is a given sql statement vulnerable to a sql injection attack?

I'm building a sql statement like the one below in a rails app :-
bank_ids = params[:bank_ids] # comes from end user or simply, is a user input.
sql_string = "SELECT * FROM users WHERE bank_id IN (#{bank_ids});"
Is the sql statement above vulnerable to an injection attack, input 'bank_ids' is end user controlled.
Take as example a table designed to store a boolean value to tell if a user is admin or not (might not happend, but it's an example):
Table "public.users"
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
------------+--------------------------------+-----------+----------+-----------------------------------+----------+--------------+-------------
id | bigint | | not null | nextval('users_id_seq'::regclass) | plain | |
name | character varying | | | | extended | |
admin | boolean | | | | plain | |
bank_id | integer | | | | plain | |
If you receive something like this:
'1) or id IN (select id from users where admin = true'
And that's interpolated afterward, then the select clause asking for the admin users will retrieve data that otherwise wouldn't appear. The query would be executed as it's built:
select * from users where bank_id IN (1) or id IN (select id from users where admin = true)
Is better for you to rely on the ORM you have at hand and leave it to do the sanitization and proper bindings for you (it's one of the reasons why those tools exist). Using ActiveRecord for example would bind the passed values for you, without having to do much:
User.where(bank_id: '1) or id IN (select id from users where admin = true')
# ... SELECT "users".* FROM "users" WHERE "users"."bank_id" = $1 [["bank_id", 1]]

MySQL: How to make sure update is always executed before select?

I am creating a web app that lets N number of users to enter receipt data.
A set of scanned receipts is given to users, but no more than 2 users should work on the same receipt.
i.e. User A and User B can work on receipt-1, but User C can not work on it(Another receipt, say receipt-2, should be assigned to the User C).
The table structure I am using looks similar to the following.
[User-Receipt Table]
+------------+--------------+
| user_id | receipt_id |
+------------+--------------+
| 000000001 | R0000000000 |
| 000000001 | R0000000001 |
| 000000001 | R0000000002 |
| 000000002 | R0000000000 |
| 000000002 | R0000000001 |
+------------+--------------+
[Receipt Table]
+-------------+--------+
| receipt_id | status |
+-------------+--------+
| R0000000000 | 0 |
| R0000000001 | 1 |
| R0000000002 | 0 |
| R0000000003 | 2 |
+-------------+--------+
★status 0:not assigned 1:assigned to a user 2: assigned to 2 users
select receipts from the receipt table whose status is not equal to '2'
insert the receipts fetched from the step 1 along with a user to whom receipts are assigned.
update the receipt status(0->1 or 1->2)
This is how I plan to achieve the above requirement.
The problem with this approach is that there could be a chance that the select(step1) is executed right before the update(step3) is executed.
If this happens, the receipts with status 2 might be fetched and assigned to another user, which does not meet the requirement.
How can I make sure that this does not happen?
For all purposes, use transactions :
START TRANSACTION
your SQL commands
COMMIT
Transactions either let all your statements executed or not executed at all and performs implicitly a lock on the updated row which is more efficient than the second approach
You can also do it using LOCK TABLE

MySQL : Use Variable/Function Returned Value in Select

There is a table named as customer like this:
| Name | Age | Balance |
------------------------------
| Kevin | 25 | 150000 |
| Bob | 33 | 350000 |
| Anna | 27 | 200000 |
Simply, to select "Name" column we can use:
SELECT Name FROM customer
Now, I want to do that by using a variable like this:
SET #temp = 'Name'
SELECT #temp FROM customer
The result I get:
| #temp |
-----------
| Name |
The result I want is same like the normal select:
| Name |
----------
| Kevin |
| Bob |
| Anna |
I am expecting this will run the same like "SELECT Name From Customer", so it basically run the SELECT from a variable value.
I also use a function returned value to do the same thing, but I get the similar result. For example, there is function called CustName(Value):
SELECT CustName(A) // Return : 'Name'
FROM customer;
This will give me result:
| CustName(A) |
-----------------
| Name |
Is there any way that MySQL will run "Name" normally like when I basically write "Select Name from customer" ?
What you're saying you're looking for is dynamic sql.. it's generally not a fabulous idea as you're trying to vary a part of a query that the database wants to be fixed, for performance reasons. You'll also struggle to make use of your sql in a client app if it's expecting a string of a username, but then the user supplied 'birthday' as the thing to select and your client gets a date instead
If you're hell bent on doing it, this SO post gives more detail: How To have Dynamic SQL in MySQL Stored Procedure
I must ask you to consider though, that this is a broken solution you've devised, to some other problem. It might be better to post the other problem as solving it may prove more productive

mysql user row level access

Have a database with the following
id | userid | name
1 | 1 | John
2 | 1 | John
3 | 2 | Joe
4 | 2 | Joe
5 | 2 | Joe
6 | 3 | Sue
7 | 3 | Sue
I need to get a way that I can create a database, then create users. Each user that I create in mysql limit them to access of data for their userid. Every database table in the database has the userid value.
So whether they are reading ,updating, insert or delete. If it is going through a specific mysql user that I attached to that database, I want that user to only read, update, insert or delete where their userid is.
I have read some things on mysql triggers but have not found anything that will work for me.
We have a backend that has data in it and restricted by userid.
The website pulls data from that table based in userid so select * from articles where userid=1. Right now, that code is modifiable by the user. I would like a way to go select * from articles and mysql only results rows that have userid=1 for that mysql user. The goal would be for every user to have their own mysql user login to the mysql database that would restrict to that specific value of userid that is theirs.
Any thoughts? Thanks so much!
GoogleResult[0] has this:
http://www.sqlmaestro.com/resources/all/row_level_security_mysql/
Abstract
The article contains a step-by-step guide to implementation of row level security in MySQL 5.0 and higher using such MySQL features as views and triggers.
Well! i will suggest to make a table for that. For the whole application
user_rights
id | user_id | insert | update | delete | read
1 | 2 | 0 | 0 | 0 | 1
Note : 1 for allowed and 0 for disallowed.
Now before you do anything first check the rights then perform other actions.
Detailed method including parts of application :
screens
id | title
1 | articles
2 | blog
user_rights
id | user_id | insert | update | delete | read | screen_id
1 | 2 | 1 | 0 | 0 | 1 | 1
2 | 2 | 0 | 0 | 0 | 1 | 2
In this method you can allow screen level access. User id 2 can add and view articles and he can aslo view blog but.
I may be using inappropriate terms here
but i hope you get the idea.

How to Implement Message feature in website

I have a table
____________________________________________________________
| Message |
|___________________________________________________________|
| Sender | Message | Receiver | message_id |
| John | How Are You?| Will | 1 |
|_________|_____________|___________________|_______________|
In Will computer I am showing as follows
Message By:John
Message:How Are You?
Now Suppose John Deleted this message But i can't remove the record because Will has not yet deleted.Therefore I am running a update query
Update Message Set Sender='' where message_id=1;
But after doing that in will computer message changes as follows
Message By: //Because i have changed sender to null
Message:How Are You?
Now how can i overcome this error of not getting desired output
Just maintain two more column boolean is_deleted(true/false) and deleted_by(S/R) to maintain the message status deleted (true or false) and deleted_by (Sender or Receiver).
An alternative to the other answers (which both could definitely work) would be to have a separate "Inbox" table. Something that just linked up which messages were still in whose inbox, like:
__________________________
| INBOX |
|________________________|
| User | Message_id |
| John | 1 |
| Will | 1 |
|_________|______________|
Then when John deletes the message you delete that row from the inbox table:
__________________________
| INBOX |
|________________________|
| User | Message_id |
| Will | 1 |
|_________|______________|
This way is also nice because it separates the metadata that makes up a message from the info about where it is stored, what actions people have taken, etc.
Add seperate boolean ReadBySender and ReadByReciever columns and update those rather than the actual sender/reciever
Just include two more (enumeration) columns to indicate, who already deleted the message.
Then in order to show the message, make sure in your WHERE clause, that the respective value is not set already.
In any case retain the Sender and Receiver columns, so that you can output all message information and attach the message to the correct users.