Mysql add column from different table - mysql

I have two tables:
| USER |
| _id | - int id PK
| id | - String id
| name |
| SUBSCRIBE |
| user |
| subscribe |
and I want to extract this data:
| _id | id | name | subscribe
Simple example
| USER |
| 1 | 10212551 | Mike
| 2 | 21022145 | Nick
| SUBSCRIBE |
| 1 | 2
get_info($user, $sub );
| EXPECTED RESULT |
| $user | id | name | 0 or 1 (false or true, if $sub has subscribed to $user)
My best try was with count():
SELECT u._id,u.id,u.name,u.email,u.country,count(s.subscribe) AS subscribe
FROM user u,subscribe s
WHERE u._id='$user' AND s.subscribe='$user' AND s.user='$sub'
But this works only if there's any record in subscribe table. So for
get_info(2,1)
it will give right result but for:
get_info(1,2)
there's nothing :/
Can somebody please help me with this ?

Use left join in order to return user even if he has not subscribed and use case to check for subscribed
select u._id,u.id,u.name,u.email,u.country,
case when s.subscribe is not null then 1 else 0 end as subscribe
from user u
left join subscribe s on (u._id = s.user and s.subscribe ='$sub')
where u._id='$user'
Demo

Related

How to design schema, display a message to it's owner, but if owner is unspecified, display to all user?

I have the schema like this,
message
+------------+----------+
| id | text |
+------------+----------+
| 1 | message1 |
| 2 | message2 |
| 3 | message3 |
+------------+----------+
user_message
+------------+-------------+
| user_id | message_id |
+------------+-------------+
| 1 | 1 |
| 1 | 2 |
| 2 | 2 |
+------------+-------------+
because the message3 is no owner, it owned to all user.
So, the user1 has message1, message2 and message3,
the user2 has message2 and message3.
And I need write the sql to query user1's messages,
SELECT
*
FROM
message AS a
JOIN
user_message AS b ON a.id = b.message_id AND b.user_id = 1
UNION ALL
SELECT
*
FROM
message AS a
LEFT JOIN
user_message AS b ON a.id = b.message_id
WHERE
b.user_id IS NULL
Does this design correct?
Or I should add the message3 to all users, like this?
+------------+-------------+
| user_id | message_id |
+------------+-------------+
| 1 | 1 |
| 1 | 2 |
| 2 | 2 |
| 1 | 3 |
| 2 | 3 |
+------------+-------------+
But if I have a new user, I wish the new user own message3, I need to write extra code to do this.
How do I do it correctly?
EDIT:
I made a mistake above, I lost a case is one user can has many messages.
As suggested by Neville Kuyt.
I like the "no surprises".
And I change the column name and schema to
message
+------------+----------+
| id | text |
+------------+----------+
| 1 | message1 |
| 2 | message2 |
| 3 | message3 |
+------------+----------+
user_message
+------------+-------------+-------------+
| id | user_id | message_id |
+------------+-------------+-------------+
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 2 | 2 |
| 4 | null | 3 |
+------------+-------------+-------------+
Now, the query will be
SELECT
*
FROM
user_message AS a
JOIN
message AS b ON a.message_id = b.id
WHERE
user_id = 1 OR user_id IS NULL;
"Correct" is a tricky thing to get right in schema design. I generally favour the "no surprises" approach - someone should be able to understand what's going on by looking at the schema and the data, without reading the documentation. I also favour "don't repeat yourself".
You offer two candidate solutions.
The first solution contains a surprise - columns with the name "id" are usually the primary key for a table; in this case, the "id" column is actually a foreign key to the "users" table. To reduce this level of surprise, I'd create the column "user_id" to contain the foreign key. If "user_id" is also the primary key in your business domain, you can just rename the column.
The second surprise is that the column contains foreign key data to the user table which don't exist, but which invoke special behaviour - they are sent to all users. A less surprising solution would be for that value to be "null", rather than a non-existent value. When you create user 3, you update the appropriate record in message.
Your schema then becomes
message
+----------------+----------+
| id |user_id | text |
+-------+--------+----------+
| 1 | 1 | message1 |
| 2 | 2 | message2 |
| 3 | null | message3 |
+-------+--------+----------+
Your second option contains another surprise - data in "messages" changes as a side-effect of a change to "user" (when you create a new user, you have to delete all the messages to other users with the ID of that user). It has the benefit of being explicit - every message/user combination is stored, but I don't like the side-effect.
You can use exists and not exists:
select m.*
from message m
where exists (select 1
from user u
where u.message_id = m.id and
u.user_id = 1
) or
not exists (select 1
from user u
where u.message_id = m.id
);

Joining three tables to get a list of tags

I have these three tables:
user_submitted_value
id | owner_id | value |
-----------------------
1 | 1 | 1337 |
2 | 2 | 1337 |
3 | 2 | 1337 |
4 | 1 | 1337 |
tag
id | owner_id | text |
---------------------------
1 | 1 | 'Tag 01' |
2 | 1 | 'Tag 02' |
3 | 1 | 'Tag 03' |
4 | 2 | 'Tag 04' |
user_submitted_value_tag
id | owner_id | tag_id | value_id |
-----------------------------------
1 | 1 | 1 | 1 |
2 | 1 | 2 | 1 |
3 | 1 | 3 | 1 |
So basically, users can submit values and enter any number of freetext tags to attach to that value. I need to store the tags as belonging to a specific user, and I need to be able to count how many times they've used each tag.
What I want to accomplish is a query that gets rows from user_submitted_value with the tags appended onto them. For example:
Query value with id 1:
id | owner_id | value | tags |
------------------------------------------------------
1 | 1 | 1337 | "'Tag 01','Tag 02','Tag 03'" |
Query all values belonging to user with id 1:
id | owner_id | value | tags |
------------------------------------------------------
1 | 1 | 1337 | "'Tag 01','Tag 02','Tag 03'" |
4 | 1 | 1337 | "" |
I know I need to JOIN one or more times, somehow, but I am not comfortable enough with SQL to figure out exactly how.
This seems like a rather arcane data format -- particularly because owner_id is repeated in all the tables.
In any case, I think the basic query that you want to get the values and tags for a given user looks like this:
select usv.owner_id,
group_concat(distinct usvt.value_id) as values,
group_concat(distinct t.text) as tags
from user_submitted_value usv join
user_submitted_value_tag usvt
on usv.value_id = usvt.value_id and usv.owner_id = usvt.owner_id join
tags t
on usvt.tag_id = t.id and usvt.owner_id = t.owner_id
group by usv_owner_id;
Here's the final solution in my case. Heavily based on the answer submitted by Gordon Linoff.
SELECT
user_submitted_value.id,
user_submitted_value.creator_id,
user_submitted_value.value,
group_concat(tag.text) AS tags
FROM user_submitted_value
LEFT JOIN user_submitted_value_tag
ON user_submitted_value.id = user_submitted_value_tag.value_id
AND user_submitted_value.creator_id = user_submitted_value_tag.creator_id
LEFT JOIN tag
ON user_submitted_valuetag.tag_id = tag.id
AND user_submitted_value_tag.creator_id = tag.creator_id
WHERE user_submitted_value.id = ?
GROUP BY user_submitted_value.id
The WHERE clause on the second JOIN can be modified to get all values for a given user.

How to insert into table based on other one?

I have these two tables:
// users
+----+-------+-----------------------+--------+
| id | name | email | active |
+----+-------+-----------------------+--------+
| 1 | peter | peter12#hotmail.com | NULL |
| 2 | jack | most_wanted#gmail.com | NULL |
| 3 | john | john_20016#yahoo.com | NULL |
+----+-------+-----------------------+--------+
// activate
+----+---------+---------------------+
| id | post_id | random_string |
+----+---------+---------------------+
| 1 | 2 | fewklw23523kf |
+----+---------+---------------------+
Also I have these two variables:
$random string = 'gergflkw4534l';
$email = 'peter12#hotmail.com';
And I want to insert a new row into activate table based on those two variables. Here is expected result:
// activate
+----+---------+---------------------+
| id | post_id | random_string |
+----+---------+---------------------+
| 1 | 2 | fewklw23523kf |
| 2 | 1 | gergflkw4534l |
+----+---------+---------------------+
As you see, I get post_id from users table based on email column. How can I do that?
INSERT INTO activate VALUES (null, /* I don't know */, :random_string)
INNER JOIN users ON /* I don't know */
WHERE email = :email
Your syntax is very much off. You need a WHERE clause before you can actually JOIN anything to it. I'm going to assume that the id column in the Activate table is an autonumber column.
INSERT INTO Activate (post_id, random_string)
SELECT
id,
:random_string
FROM
Users
WHERE
email = :email
You probably want to use the mysql insert-select syntax:
INSERT INTO activate (post_id, random_string)
SELECT id, :random_string
FROM users
WHERE email = :email
LIMIT 1;

Retrieve all not logged in users from mysql tables (SQL query)

I have 2 tables listed below:
+-------------+-----------+--------------+-------------------------------+
| v3_customer |
+-------------+-----------+--------------+-------------------------------+
| customer_id | firstname | lastname | email |
+-------------+-----------+--------------+-------------------------------+
| 1 | Piet | Pizza | piet.pizza#example.com |
| 2 | Klaas | Hein | klaas.hein#example.com |
| 3 | Henk | Crowdcontrol | henk.crowdcontrol#example.com |
+-------------+-----------+--------------+-------------------------------+
+-------------+-------------+---------------+
| v3_customer_activity |
+-------------+-------------+---------------+
| activity_id | customer_id | key |
+-------------+-------------+---------------+
| 1 | 1 | login |
| 2 | 1 | order_account |
| 3 | 2 | login |
+-------------+-------------+---------------+
What i want is to select all customers which haven't logged in yet (note the login key in v3_customer_activity). So in this case that would be the customer with customer_id: 3
I'm using a mysql database.
I have tried using the following query:
SELECT DISTINCT v3_customer.customer_id, v3_customer.firstname, v3_customer.lastname, v3_customer.email FROM `v3_customer` JOIN v3_customer_activity ON v3_customer.customer_id = v3_customer_activity.customer_id WHERE v3_customer.customer_id != (SELECT v3_customer_activity.customer_id FROM v3_customer_activity)
In the hope it would iterate between the rows found in the subquery.
This resulted in an error telling me a subquery may not contain multiple rows.
TL;DR
What I want is to retrieve every customer from v3_customer who is not listed in the table v3_customer_activity
Try this:
select v3_customer.* from v3_customer
left join v3_customer_activity on v3_customer.customer_id=v3_customer_activity.customer_id
where v3_customer_activity.customer_id is null;
Left join v3_customer table with v3_customer_activity and filter records which are not matched.
select v3_customer.* from v3_customer
where v3_customer.customer_id not in (SELECT v3_customer_activity.customer_id FROM v3_customer_activity)

MySQL: GROUP BY with custom hierarchical functionality

I've got a permission/privileges - table looking like this:
+----+----------+----------+------+-------+
| id | name | usertype | read | write |
+----+----------+----------+------+-------+
| 1 | test | A | 0 | 0 |
| 2 | test | MU | 1 | 1 |
| 3 | test | U | 1 | 1 |
| 4 | apple | A | 1 | 1 |
| 5 | apple | MU | 1 | 0 |
| 6 | apple | U | 0 | 0 |
| 7 | flower | A | 0 | 0 |
| 8 | flower | MU | 0 | 0 |
| 9 | flower | U | 1 | 1 |
+----+----------+----------+------+-------+
there are 3 usertypes: A (admin), MU (maintenance user), U (standard user)
the usertypes are hierarchical: A > MU > U
(the usertypes are saved as CHAR(2) in the database, and unfortunately I can't change that)
now i want to build a query which implements the hierarchical logic of my usertypes.
e.g. usertype 'A' got no permission to read or write on stuff with the name 'test', thus usertypes 'MU' AND 'U' also should have no permission for that and their read = 1 and write = 1 should be ignored.
I know which usertype is currently logged in.
I somehow have to check for the minimum of read/write rights to the name for all hierarchical predecessors, i guess. but i don't know how to check that since usertype is not a number field.
this is what I've tried so far:
SELECT
name,
MIN(read),
MIN(write),
CASE
WHEN usertype = 'A' THEN 0
ELSE (CASE
WHEN usertype = 'WU' THEN 1
ELSE 2
END)
END userval
FROM
permissions
-- WHERE usertype <= :current_usertype
GROUP BY name
this seems to work, but i don't know how i can get my condition WHERE usertype <= :current_usertype working, so a usertype down in the hierarchy can't get more privileges on a name than a "higher" usertype.
any ideas?
thanks in advance!
This is how I solved my problem:
1. I added another table "permission_groups" to the database:
+----+----------+--------+
| id | usertype | value |
+----+----------+--------+
| 1 | A | 100 |
| 2 | MU | 20 |
| 3 | U | 10 |
+----+----------+--------+
2. Then I joined this table to my original table "permissions" which i showed in my question:
here i get the value of my "permission_groups" table with a subquery. this value symbolizes the hierarchical order of my different usertypes.
SELECT
perm.name,
MIN(perm.`read`),
MIN(perm.`write`),
group .value
FROM
permissions perm
LEFT JOIN permission_groups group ON group.usertype = perm.usertype
WHERE
group.value >= (SELECT value from permission_groups WHERE usertype = :current_usertype)
GROUP BY perm.name
:current_usertype is a PDO parameter in my case, which is replaced by the usertype of the current user.