UNION query functionality and the query structure - mysql

What I'm trying to achieve is to insert values into a table if that value does not exist in 2 extra tables.
INSERT INTO visitor(
visitor_username,
email,
PASSWORD
)
SELECT * FROM
(
SELECT
'admin2000',
'adminemail#mail.com',
'123456'
) AS tmp
WHERE NOT EXISTS
(
SELECT
admin.admin_username,
admin.email
FROM
admin AS admin
WHERE
admin.admin_username = 'admin2000' AND admin.email =
'adminemail#mail.com'
UNION
SELECT
staff.staff_username,
staff.email
FROM
staff AS staff
WHERE
staff.staff_username = 'admin2000' AND staff.email =
'adminemail#mail.com'
)
LIMIT 1
In the WHERE NOT EXIST part when I only ask for *_username (example: admin_username or staff_username) it works well, but when I need to verify if the email exists too it does not work as intended.
Am I using WHERE NOT EXIST properly? if the username 'admin2000' and email 'adminemail#mail.com' exist on the table 'admin' and I'm trying to insert it into 'visitor' table it inserts it and it should not be doing that.

The problem is the AND in the subqueries. It searches for rows that have that username AND that email. So if you have an admin called admin2000, but with a different e-mail address, that admin won't be returned by the subquery, and so the new row will be inserted.
Use OR instead of AND, and the the problem will be solved.

You seem to want to be writing a query like this:
INSERT INTO visitor (visitor_username, email, PASSWORD)
SELECT t.*
FROM (SELECT 'admin2000' as visitor_username, 'adminemail#mail.com' as email, '123456' as PASSWORD
) t
WHERE NOT EXISTS (SELECT 1
FROM admin a
WHERE a.visitor_username = t.visitor_username AND a.email = t.email
)
UNION
SELECT s.staff_username, s.email, ? as password
FROM staff s
WHERE s.staff_username = 'admin2000' AND s.email =
'adminemail#mail.com';
Note that the second subquery is missing a password, so there is an error.
This seems more concisely written using a single query:
INSERT INTO visitor (visitor_username, email, PASSWORD)
SELECT t.*
FROM (SELECT 'admin2000' as visitor_username, 'adminemail#mail.com' as email, '123456' as PASSWORD
) t
WHERE NOT EXISTS (SELECT 1
FROM admin a
WHERE a.admin_username = t.visitor_username AND a.email = t.email
) AND
NOT EXISTS (SELECT 1
FROM staff s
WHERE s.staff_username = t.visitor_username AND s.email = t.email
);

Related

Mysql - Fetch data from EXISTS (SELECT..) statement aswell

I have a mysql query that returns user_name and sess_start from sess_active table if user_name exists in sess_active_tmp table.
Query below for above is working as inteded, but I also would like to retrieve data from sess_active_tmp table aswell. Since I execute another SELECT statement in EXISTS part, shouldn't I be able to retrieve data of this statement aswell?
Working query:
SELECT sess_main.user_name,
sess_main.sess_start
FROM sess_active AS sess_main
WHERE ( sess_main.user_name = 'testuser'
AND sess_main.sess_stop IS NULL )
AND EXISTS (SELECT user_name
FROM sess_active_tmp AS sess_temp
WHERE sess_temp.user_name = 'testuser'
AND sess_temp.sess_start =
sess_main.sess_start
AND sess_temp.sess_stop IS NULL)
Query I am trying to get it work;
SELECT sess_main.user_name,
sess_main.sess_start,
sess_temp.upload_byte,
sess_temp.download_byte,
FROM sess_active AS sess_main
WHERE ( sess_main.user_name = 'testuser'
AND sess_main.sess_stop IS NULL )
AND EXISTS (SELECT user_name
FROM sess_active_tmp AS sess_temp
WHERE sess_temp.user_name = 'testuser'
AND sess_temp.sess_start =
sess_main.sess_start
AND sess_temp.sess_stop IS NULL)
I also added sess_temp.upload_byte, sess_temp.download_byte to sess_temp select statement but it also doesn't work.
Is it only achievable with JOIN and if yes would it decrease performance? since this query will run like 2-3k times(users) every 2 minutes.
Thanks in advance.
EDIT: Query below does what I want to achieve but I have performance concerns, is it the best way to achieve what I want to do?
SELECT sess_main.user_name,
sess_main.sess_start,
sess_temp.upload_byte,
sess_temp.download,
FROM sess_active AS sess_main
INNER JOIN sess_active_tmp sess_temp
ON ( sess_temp.user_name = sess_main.user_name
AND sess_temp.sess_start = sess_main.sess_start
AND sess_temp.sess_stop IS NULL )
WHERE ( sess_main.user_name = 'testuser'
AND sess_main.sess_stop IS NULL )
AND EXISTS (SELECT user_name
FROM sess_active_tmp AS sess_temp
WHERE sess_temp.user_name = 'testuser'
AND sess_temp.sess_start = sess_main.sess_start
AND sess_temp.sess_stop IS NULL)

Get users who don't have sent email MySQL

My DB has the following tables,
User (id, name)
EmailTemplate (id, subject, template)
EmailTrack (id, user_id, email_template_id)
I want to get the users who don't have sent specific email_template yet by the SQL query.
You could use the not exists operator:
SELECT *
FROM user u
WHERE NOT EXISTS (SELECT *
FROM emailtemplate temp
JOIN emailtrack track ON temp.id = track.email_template_id
WHERE template = 'some_specific_template' AND
track.user_id = u.id)

Sql renaming several raw data

I have been through some solutions published here, but none of them solved my problem.
I want to set my username column to receive now unique usernames.
But for that I need to rename in certain situations, more than 1000 duplicated usernames already registered.
I tried this solution:
UPDATE profile n
JOIN (SELECT username, MIN(profile_id) min_id FROM profile
GROUP BY username HAVING COUNT(*) > 1) d
ON n.username = d.username AND n.profile_id <> d.min_id SET n.username = CONCAT(n.username, '1');
But it gives me for the same user name for example tony, tony1, tony11, tony111 and so on up to tony1111111111111... up to 1000, make the username have a long long lenght.
I would like a solution to get only up 4digites after the username word. 0001,0002,0003,0004....1000
Can somebody help me here?
Thank you in advance
how about something like:
UPDATE profile n JOIN (
SELECT profile_id, username, (#row_number:=#row_number+1) as cntr
FROM profile, (SELECT #row_number:=0) AS t
WHERE username IN ( SELECT username
FROM profile
GROUP BY username HAVING COUNT(*) > 1 )
and (username, profile_id) not in ( SELECT username, MIN(profile_id)
FROM profile
GROUP BY username HAVING COUNT(*) > 1 )
) d ON n.profile_id = d.profile_id
SET n.username = CONCAT(n.username, d.cntr);
This is the best I can come up with at the moment.... the problem is that it will share the counter between all usernames... you you will have Alejandro, Alejandro1, Pedro, Pedro2, Juan, Juan3 .....
I believe that this that you've commented is wrong...No Update. Only select: select * from profile n JOIN ( SELECT username, min_id, #row_number:=#row_number+1 as cntr FROM ( SELECT username, MIN(profile_id) min_id FROM profile GROUP BY username HAVING COUNT(*) > 1 ) AS t2 , (SELECT #row_number:=0) AS t ) d ON n.username = d.username

MYSQL - SELECT AS alias to be used in subquery

I want to be able to use one of the items I am selecting further down in my sub query.
As you can see in this example I have set the "variable" in question as 100 but in reality this is going to be a dynamic number so I will need to be able to access it in the subquery.
INSERT INTO users_friends (userId, friendId)
SELECT 77, id as noob
FROM users WHERE email = 'a#g.com'
AND
NOT EXISTS (SELECT * FROM users_friends
WHERE userId = 77 and friendId = noob)
LIMIT 1
I'm not completely sure I understand your question, but NOT EXISTS works just like LEFT JOIN and IS NULL. So I think this will work:
SELECT 77, #noob
FROM users u
JOIN (SELECT #noob:= 100) r
LEFT JOIN users_friends uf on u.userid = uf.userid and uf.friendid = #noob
WHERE email = 'a#g.com'
AND uf.userid IS NULL
And here is the SQL Fiddle.
I think the safest approach is to define these in a subquery. I typically give this the alias of const:
INSERT INTO users_friends (userId, friendId)
SELECT const.userId, const.noob
FROM users cross join
(select 77 as userId, 100 as noob) const
WHERE email = 'a#g.com' AND
NOT EXISTS (SELECT *
FROM users_friends
WHERE userId = const.userId and friendId = const.noob
)
LIMIT 1
I am concerned about SGeddes's approach, because it relies on the correct evaluation of variables outside the scope of the query. This might work in this case, but I prefer a solution where the query does not rely on outside variables. By the way, this should also work in any database, and is not MySQL-specific.
EDIT
I would recommend changing your table structure.
CREATE TABLE users_friends(
userid int,
friendid int,
primary key (userid, friendid)
);
CREATE TABLE users (
userid int primary key,
email varchar(100),
name VARCHAR (100),
index (email,name)
);
INSERT INTO users VALUES (1, 'a#g.com', 'noob'), (2,'b#g.com', 'Joe');
INSERT INTO users_friends (userId, friendId)
VALUES (2, (SELECT userId
FROM users
WHERE email = 'a#g.com'
AND name = "noob"
AND NOT exists (SELECT * FROM users_friends as uf
JOIN users as u
ON u.userid = uf.userid
where uf.friendid = 2 AND name = "noob"
)
)
);
SQL FIDDLE DEMO
Try this:
<?php
function select_query ($userid,$friendname, $email){
$host = "host";
$user = "username";
$password = "password";
$database = "database name";
// open connection to databse
$link = mysqli_connect($host, $user, $password, $database);
IF (!$link){
echo ("Unable to connect to database!");
}
ELSE {
//Is someone registered at other conference from table registration
$query = " INSERT INTO users_friends (userId, friendId)
VALUES (".$userId.", (SELECT userId
FROM users
WHERE email = '".$email."'
AND name = '".$friendname."'
AND NOT exists (SELECT * FROM users_friends as uf
JOIN users as u
ON u.userid = uf.userid
where uf.friendid = ".$userId." AND name = '"$friendname"'
)
)
)";
$result = mysqli_query($link, $query);
return $query;
return $result;
}
mysqli_close($link);
}
echo select_query(1,noob,'a#g.com');
?>
Like I mentioned above I not sure what you mean. If you mean dynamic in the sense that you can change the value of the variable this might help. In your previous posts you used PHP. So, my guess is that your are using PHP.
INSERT INTO users_friends (userId, friendId)
SELECT 77, id
FROM users WHERE email = 'a#g.com'
AND
NOT EXISTS (SELECT * FROM users_friends
WHERE userId = 77 and friendId = (SELECT id
FROM users WHERE email = 'a#g.com'))
LIMIT 1

Substitute for MySQL's variables in PostgreSQL?

We often use quick one-off SQL files to insert or update data in an existing database. The SQL is usually written by a developer, tested on the development system, and then imported in the production DB with psql -U dbuser dbname < file.sql.
A (trivial) example might look like this:
INSERT INTO employees (
company_id,
name,
position,
created_by,
last_modified_by
) VALUES
(
(SELECT id FROM companies WHERE name = 'Acme Fellowship'),
'Frodo Baggins',
'Ring bearer',
(SELECT id FROM users WHERE login = 'admin'),
(SELECT id FROM users WHERE login = 'admin')
),
(
(SELECT id FROM companies WHERE name = 'Acme Fellowship'),
'Samwise Gamgee',
'Rope bearer',
(SELECT id FROM users WHERE login = 'admin'),
(SELECT id FROM users WHERE login = 'admin')
),
(
(SELECT id FROM companies WHERE name = 'Acme Fellowship'),
'Peregrin Took',
'Ent rider',
(SELECT id FROM users WHERE login = 'admin'),
(SELECT id FROM users WHERE login = 'admin')
);
While this works, there's a lot of repetitive code in the subqueries. It would be nice (more efficient and less error prone) to store the relevant values for companies.id and users.id in temporary variables. In this construed example, the performance difference is likely minimal, but in practice we do have more complex queries and updates, and there are often more than three updated/inserted records.
The same example written for MySQL looks like this:
SELECT #company_id := id FROM companies WHERE name = 'Acme Fellowship';
SELECT #admin_id := id FROM users WHERE login = 'admin';
INSERT INTO employees (
company_id,
name,
position,
created_by,
last_modified_by
) VALUES
(#company_id, 'Frodo Baggins', 'Ring bearer', #admin_id, #admin_id),
(#company_id, 'Samwise Gamgee', 'Rope bearer', #admin_id, #admin_id),
(#company_id, 'Peregrin Took', 'Ent rider', #admin_id, #admin_id);
Is there any way to achieve something similar in PostgreSQL?
What I've looked at:
psql's session variables (with \set): cannot be used to store query results
plpgsql: can only be used in a procedure (we're still running 8.4)
temporary tables: I can't see how to use them without creating ugly and convoluted statements
If there is no direct equivalent for Postgres, what do you think would be the least clumsy way to produce update files of this kind?
Use VALUES() in a SELECT, that should work:
INSERT INTO employees (
company_id,
name,
position,
created_by,
last_modified_by
)
SELECT
(SELECT id FROM companies WHERE name = 'Acme Fellowship'),
name,
position,
(SELECT id FROM users WHERE login = 'admin'),
(SELECT id FROM users WHERE login = 'admin')
FROM
(VALUES -- all your new content here
('Frodo Baggins', 'Ring bearer'),
('Samwise Gamgee', 'Rope bearer'),
('Peregrin Took', 'Ent rider')
) content(name, position); -- use some aliases to make it readable
Consider using CTEs or subqueries to query values once and inserted them many times.
This way, you can replace MySQL style variables with standard SQL.
INSERT INTO employees
(company_id, name, position, created_by, last_modified_by)
SELECT c.id , name, position, u.id , u.id
FROM (SELECT id FROM companies WHERE name = 'Acme Fellowship') c
,(SELECT id FROM users WHERE login = 'admin') u
,(VALUES
('Frodo Baggins', 'Ring bearer')
,('Samwise Gamgee', 'Rope bearer')
,('Peregrin Took', 'Ent rider')
) v(name, position)
Assuming that companies.name and users.login are, in fact, unique. Multiple hits would multiply the rows to be inserted.
Read about the INSERT command in the manual.
Here is my test setup with temporary tables in case anyone wants to have a quick look:
CREATE TEMP TABLE companies (id int, name text);
INSERT INTO companies VALUES (17, 'Acme Fellowship');
CREATE TEMP TABLE users (id int, login text);
INSERT INTO users VALUES (9, 'admin');
CREATE TEMP TABLE employees (
company_id int
,name text
,position text
,created_by int
,last_modified_by int);
This is an old question, but I found that using WITH statements made my life easier :)
WITH c AS (
SELECT company_id,
FROM companies
WHERE name = 'Acme Fellowship'
), u AS (
SELECT *
FROM users
WHERE login = 'admin'
), n AS (
SELECT *
FROM
(VALUES -- all your new content here
('Frodo Baggins', 'Ring bearer'),
('Samwise Gamgee', 'Rope bearer'),
('Peregrin Took', 'Ent rider')
) content(name, position)
)
INSERT INTO employees (
company_id,
name,
position,
created_by,
last_modified_by
)
SELECT c.company_id, n.name, n.position, u.id, u.id
FROM c, u, n