I have a table that looks like this:
CREATE TABLE `relations` (
`idA` VARCHAR(20),
`idB` VARCHAR(20).
PRIMARY KEY (idA,idB)
)
TYPE=MyISAM;
and it basically just maps two ids together from another table that looks like this:
CREATE TABLE `scores` (
`id` VARCHAR(20) PRIMARY KEY,
`score` INT(10) UNSIGNED NOT NULL DEFAULT ‘0’,
`friendsids` VARCHAR(1000)
)
TYPE=MyISAM;
So - if I want to add something to the relations table, I query
INSERT IGNORE INTO relations VALUES ('$idA', '$idB')
So the problem - it sometimes creates entries that have the same info but swapped between idA and idB. For example, if one entry is idA = 1, idB = 2 - I dont' want an entry that looks like idA = 2, idB = 1;
I tried:
INSERT IGNORE INTO relations VALUES ('$idA', '$idB') WHERE NOT EXISTS (SELECT * WHERE idA IS '$idB' AND idB IS '$idA');
It gives me a syntax error which I somehow can't figure out:
Query failed: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'IGNORE INTO relations VALUES ('saubua', 'deppata') WHERE NOT EXISTS (SELECT * WH' at line 1
Is there a chance I'm totally on the wrong way with this? Is there a simpler way?
can you try
INSERT INTO relations
SELECT '$idA','$idB'
FROM dual
WHERE NOT EXISTS (SELECT idA
FROM relations
WHERE (idA='$idA' OR idB='$idB')
OR (idA='$idB' OR idB='$idA')
)
so - I took Akhils code and mofified it, so not it works:
INSERT IGNORE INTO relations SELECT '$idA','$idB' FROM dual WHERE NOT EXISTS (SELECT idA FROM relations WHERE (idA='$idB' AND idB='$idA') )
thanks everyone!! :)
Related
I have 3 tables course, grade and evaluation. I want comparing two tables grade and evaluation . if the data in the table grade does not exist in the table evaluation , then the data will appear (output)
" select Grade.ID_Courses,Course.ID_Courses,Grade.NAME,
Course.NAME, Grade.ID_Courses,
Evaluation.NAME,
Evaluation.Year,
Grade.Year
from Grade, Course, Evaluation
WHERE
Grade.ID_Courses=Course.ID_Courses AND
Grade.NAME=JOHN and
Grade.Year=1 and
Evaluation.NAME=GRADE.NAME and
Grade.ID_Courses NOT IN (SELECT ID_Courses FROM Evaluation where NAME=JOHN and Year=1 )
GROUP BY Grade.ID_Courses"
the problem is when the name john is not in the table evaluation then there is no output comes out .
Avoid NOT IN like the plague if
SELECT ID_Courses FROM Evaluation where `NAME`='JOHN' and Year=1
could ever contain NULL. Instead, use NOT EXISTS or Left Joins
use explicit joins, not 1980's style joins using the WHERE clause
To illustrate the misery of NOT IN:
SQL NOT IN () danger
create table mStatus
( id int auto_increment primary key,
status varchar(10) not null
);
insert mStatus (status) values ('single'),('married'),('divorced'),('widow');
create table people
( id int auto_increment primary key,
fullName varchar(100) not null,
status varchar(10) null
);
Chunk1:
truncate table people;
insert people (fullName,`status`) values ('John Henry','single');
select * from mstatus where `status` not in (select status from people);
** 3 rows, as expected **
Chunk2:
truncate table people;
insert people (fullName,`status`) values ('John Henry','single'),('Kim Billings',null);
select * from mstatus where status not in (select status from people);
no rows, huh?
Obviously this is 'incorrect'. It arises from SQL's use of three-valued logic,
driven by the existence of NULL, a non-value indicating missing (or UNKNOWN) information.
With NOT IN, Chunk2 it is translated like this:
status NOT IN ('married', 'divorced', 'widowed', NULL)
This is equivalent to:
NOT(status='single' OR status='married' OR status='widowed' OR status=NULL)
The expression "status=NULL" evaluates to UNKNOWN and, according to the rules of three-valued logic,
NOT UNKNOWN also evaluates to UNKNOWN. As a result, all rows are filtered out and the query returns an empty set.
Possible solutions include:
select s.status
from mstatus s
left join people p
on p.status=s.status
where p.status is null
or use not exists
Try using joins to solve this
select g.*, e.*,c.* from
grade g inner join evaluation e on
g.ID_COURSES <> e.ID_COURSES and g.year <> e.year
inner join COURSE c on c.ID_COURSES = g.ID_COURSES
;
I want to execute a query where I can find one ID in a list of ID.
table user
id_user | name | id_site
-------------------------
1 | james | 1, 2, 3
1 | brad | 1, 3
1 | suko | 4, 5
and my query (doesn't work)
SELECT * FROM `user` WHERE 3 IN (`id_site`)
This query work (but doesn't do the job)
SELECT * FROM `user` WHERE 3 IN (1, 2, 3, 4, 6)
That's not how IN works. I can't be bothered to explain why, just read the docs
Try this:
SELECT * FROM `user` WHERE FIND_IN_SET(3,`id_site`)
Note that this requires your data to be 1,2,3, 1,3 and 4,5 (ie no spaces). If this is not an option, try:
SELECT * FROM `user` WHERE FIND_IN_SET(3,REPLACE(`id_site`,' ',''))
Alternatively, consider restructuring your database. Namely:
CREATE TABLE `user_site_links` (
`id_user` INT UNSIGNED NOT NULL,
`id_site` INT UNSIGNED NOT NULL,
PRIMARY KEY (`user_id`,`site_id`)
);
INSERT INTO `user_site_links` VALUES
(1,1), (1,2), (1,3),
(2,1), (2,3),
(3,4), (3,5);
SELECT * FROM `user` JOIN `user_site_links` USING (`id_user`) WHERE `id_site` = 3;
Try this: FIND_IN_SET(str,strlist)
NO! For relation databases
Your table doesn't comfort first normal form ("each attribute contains only atomic values, and the value of each attribute contains only a single value from that domain") of a database and you:
use string field to contain numbers
store multiple values in one field
To work with field like this you would have to use FIND_IN_SET() or store data like ,1,2,3, (note colons or semicolons or other separator in the beginning and in the end) and use LIKE "%,7,%" to work in every case. This way it's not possible to use indexes[1][2].
Use relation table to do this:
CREATE TABLE user_on_sites(
user_id INT,
site_id INT,
PRIMARY KEY (user_id, site_id),
INDEX (user_id),
INDEX (site_id)
);
And join tables:
SELECT u.id, u.name, uos.site_id
FROM user_on_sites AS uos
INNER JOIN user AS u ON uos.user_id = user.id
WHERE uos.site_id = 3;
This way you can search efficiently using indexes.
The problem is that you are searching within several lists.
You need something more like:
SELECT * FROM `user` WHERE id_site LIKE '%3%';
However, that will also select 33, 333 and 345 so you want some more advanced text parsing.
The WHERE IN clause is useful to replace many OR conditions.
For exemple
SELECT * FROM `user` WHERE id IN (1,2,3,4)
is cleaner than
SELECT * FROM `user` WHERE id=1 OR id=2 OR id=3 OR id=4
You're just trying to use it in a wrong way.
Correct way :
WHERE `field` IN (list_item1, list_item2 [, list_itemX])
I have a table like this:
id | userid | commentid | value
-------------------------------
Each user is permitted to vote a comment once. The value can be between -1 and 1. Is there a easy way for a table model to achieve an change of this vote in one single query, without checking EXISTS() first? I already thought about a hash-column like this
MD5(CONCAT(userid, commentid))
but is there any better solution for this?
Just use an unique key on userid + commentid and you can use ON DUPLICATE KEY UPDATE.
INSERT INTO `yourTable` (
`userid`,
`commentid`,
`value`
) VALUES (
x,
y,
z
) ON DUPLICATE KEY UPDATE
`value` = z
I have an UPDATE query where I explicitely reference the database, but MySQL still complains with the message: ERROR 1046 (3D000): No database selected.
Other queries that are similar of structure, but use an INSERT work fine. Other queries that only perform SELECTs also run fine.
To repeat the problem in a test case, try running these queries:
create table test.object1 (
id_object1 int unsigned not null auto_increment,
total int,
weight int,
dt datetime,
primary key (id_object1)
) engine=InnoDB;
create table test.object2 (
id_object2 int unsigned not null auto_increment,
primary key (id_object2)
) engine=InnoDB;
create table test.score (
id_object1 int unsigned not null,
id_object2 int unsigned not null,
dt datetime,
score float,
primary key (id_object1, id_object2),
constraint fk_object1 foreign key (id_object1) references object1 (id_object1),
constraint fk_object2 foreign key (id_object2) references object2 (id_object2)
) engine=InnoDB;
insert into test.object1 (id_object1, total, weight, dt) values (1, 0, 0, '2012-01-01 00:00:00');
insert into test.object1 (id_object1, total, weight, dt) values (2, 0, 0, '2012-01-02 00:00:00');
insert into test.object2 (id_object2) values (1);
insert into test.score (id_object1, id_object2, dt, score) values (1, 1, '2012-01-03 00:00:00', 10);
insert into test.score (id_object1, id_object2, dt, score) values (2, 1, '2012-01-04 00:00:00', 8);
update test.object1 p
join (
select ur.id_object1, sum(ur.score * ur.weight) as total, count(*) as weight
from (
select lur.*
from (
select s.id_object1, s.id_object2, s.dt, s.score, 1 as weight
from test.score as s
join test.object1 as o1 using(id_object1)
where s.dt > o1.dt
order by s.id_object1, s.id_object2, s.dt desc
) as lur
group by lur.id_object2, lur.id_object1, date(lur.dt)
order by lur.id_object1, lur.id_object2
) as ur
group by ur.id_object1
) as r using(id_object1)
set
p.total = p.total + r.total,
p.weight = p.weight + r.weight,
p.dt = now();
Note: I'm running these queries from a PHP environment and I have NOT explicitely used mysql_select_db('test'), because I prefer not to and none of the other (many!) queries require it. I'm sure that using mysql_select_db would solve my issue, but I would like to know why exactly this particular query does not work.
For comparison sake: if you'd run this simpler query, also without using mysql_select_db, everything works fine:
update test.object1 set total=1, weight=1, dt=now() where id_object1=1;
I've searched to no avail. The only thing I found that came close, was this bug report: http://bugs.mysql.com/bug.php?id=28551 and especially that last (unanswered) message...
You have fields named incorrectly, but even if you correct them, this is a bug in MySQL that won't let you do it if you don't have default database.
update test.object1 p
join (
select ur.id_object1, sum(ur.score * ur.weight) as total, count(*) as weight
from (
select lur.*
from (
select s.id_object1, s.id_object2, s.dt, s.score, 1 as weight
from test.score as s
join test.object1 as o1
using (id_object1)
where s.dt > o1.dt
order by
s.id_object1, s.id_object2, s.dt desc
) as lur
group by
lur.id_object1, lur.id_object1, date(lur.dt)
order by
lur.id_object1, lur.id_object1
) as ur
group by ur.id_object1
) as r
USING (id_object1)
SET p.total = p.total + r.total,
p.weight = p.weight + r.weight,
p.dt = now();
The problem is specific to UPDATE with double-nested queries and no default database (SELECT or single-nested queries or default database work fine)
You have some wrong field names in the UPDATE statement -
What is s.object? Shouldn't it be s.id_object2?
What is lur.object1? Shouldn't it be lur.id_object1?
What is lur.object2? Shouldn't it be lur.id_object2?
What is ur.id_object at the end?
Fix all these issues and try to update again;-)
First time I ran this script I got that error. My output:
1 row inserted [0,184s]
1 row inserted [0,068s]
1 row inserted [0,066s]
1 row inserted [0,147s]
1 row inserted [0,060s]
Error (32,1): No database selected
When I set default database name the problem disappeared.
Remember that you cannot use foreign keys when the Engine is set to MyISAM. Not only does the table that you are creating a foreign key in need to be InnoDB, but the table you are getting the key from also needs to be InnoDB.
I was getting the same error as you and pulling my hair out for days before I thought of this. I went into each of my tables and made sure the Engines were set to InnoDB for each one, and now I have no issues setting up foreign keys.
I have 1 Mysql database with 2 tables:
DOCUMENTS
...
- staffID
.....
STAFF
- ID
- Name
The DOCUMENTS table assigns each document to a single or multiple users from the STAFF table therefore the staffID in the DOCUMENTS table consists of a comma separated array of staff ID's for example (2, 14).
I managed to split the array into individual values:
2
14
but rather than having the ID numbers I would like to have the actual names from the STAFF table - how can I achieve this. Any help would be greatly appreciated - please see my current code below.
$result = mysql_query("SELECT
organizations.orgName,
documents.docName,
documents.docEntry,
documents.staffID,
staff.Name,
staff.ID
FROM
documents
INNER JOIN organizations ON (documents.IDorg = organizations.IDorg)
INNER JOIN staff ON (documents.staffID = staff.ID)
")
or die(mysql_error());
while($row = mysql_fetch_array($result)){
$splitA = $row['staffID'];
$resultName = explode(',', $splitA );
$i=0;
for($i=0;$i<count($resultName);$i++)
{
echo "<a href='staffview.php?ID=".$row['docName'].
"'>". $resultName[$i]."</a><br>";
}
echo '<hr>';
}
It looks like your existing code might work where documents.staffID = staff.ID - that is where there is just a single staffID associated with the document?
You'd be better off adding a table to model the relationships between documents and staff separately from either, and removing or deprecating the staffID field in the documents table. You'd need something like
CREATE TABLE document_staff (
document_id <type>,
staff_id <type>
)
You can include compound indexes with ( document_id, staff_id ) and ( staff_id, document_id ) if you have lots of data and/or you want to traverse the relationship efficiently in both directions.
(You don't mention data types for your identity fields, but documents.staffID appears to be some sort of varchar based on what you say - perhaps you could use an integer type for these instead?)
But you can probably achieve what you want using the existing schema and the MySQL FIND_IN_SET function:
SELECT
organizations.orgName,
documents.docName,
documents.docEntry,
documents.staffID,
staff.Name,
staff.ID
FROM
documents
INNER JOIN organizations ON (documents.IDorg = organizations.IDorg)
INNER JOIN staff ON ( FIND_IN_SET( staff.ID, documents.staffID ) > 0 )
MySQL set types have limitations - maximum membership size of 64 for example - but may be sufficient for your needs.
If it was me though, I'd change the model rather than use FIND_IN_SET.
Thank you so much for you answer - greatly appreciated!
My table setup is:
DOCUMENTS:
CREATE TABLE documents (
docID int NOT NULL,
docTitle mediumblob NOT NULL,
staffID varchar(120) NOT NULL,
Author2 int,
IDorg int,
docName varchar(150) NOT NULL,
docEntry int AUTO_INCREMENT NOT NULL,
/* Keys */
PRIMARY KEY (docEntry)
) ENGINE = MyISAM;
STAFF:
CREATE TABLE staff (
ID int AUTO_INCREMENT NOT NULL,
Name varchar(60) NOT NULL,
Organization varchar(20),
documents varchar(150),
Photo mediumblob,
/* Keys */
PRIMARY KEY (ID)
) ENGINE = MyISAM;
The DOCUMENTS table reads via a lookup table (dropdown) from the STAFF table so that I can assign multiple staff members to a document. So I can access the staffID array in the DOCUMENTS table and split that and I wonder if there is a way to then associate the staffID with the staff.Name and print out the staff Name rather than the ID in the results of the query. Thanks again!