Violation of Primary Key constraint error - sql-server-2008

I am using SSMS 2008 and trying to insert with this query but am getting the following error:
Msg 2627, Level 14, State 1, Line 1
Violation of PRIMARY KEY constraint 'PK_j5c_MasterMeasures'. Cannot insert duplicate key in object 'dbo.j5c_MasterMeasures'.
The statement has been terminated.
Here is my query:
insert into J5C_MasterMeasures (studentid, measuredate, measureid, RIT)
select A.studentid, A.measuredate, B.measurename+' ' +B.LabelName, A.score_14
from [J5C_Measures_Sys] A
join [J5C_ListBoxMeasures_Sys] B on A.MeasureID = B.MeasureID
join sysobjects so on so.name = 'J5C_Measures_Sys'
join syscolumns sc on so.id = sc.id
join [J5C_MeasureNamesV2_Sys] v on v.Score_field_id = sc.name
where so.type = 'u' and sc.name = 'score_14' and a.score_14 is not null
AND A.STUDENTID IS NOT NULL AND A.MEASUREDATE IS NOT NULL AND B.MEASURENAME IS NOT NULL
group by a.studentid, a.measuredate, B.measurename, B.LabelName, A.score_14
--HAVING COUNT(*) > 1
The strange thing is that if I run just the SELECT query (without the INSERT) and include the HAVING COUNT statement, it returns 0 records for > 1. So I don't know where the duplicate is coming from!

Based on your earlier question, i believe that your primary key is A.studentid, A.measuredate, B.measurename. Please correct me if i am wrong on this.
Since you are grouping by two additional columns B.LabelName and A.score_14 in addition to your columns of your composite primary key, if there are any duplicates - which there can be provided they have different values of either B.LabelName or A.score_14 - you will violate your primary key constraint and this error will be thrown.
Your data will just not be unique enough to satisfy your primary key - which states that ONLY ONE ROW with a unique combination of A.studentid, A.measuredate, B.measurename can exist in your table

Is there data already in the J5C_MasterMeasures table? If so make sure that what you are inserting doesn't already exist

Use:
select A.studentid, A.measuredate, B.measurename+' ' +B.LabelName, A.score_14
from [J5C_Measures_Sys] A
join [J5C_ListBoxMeasures_Sys] B on A.MeasureID = B.MeasureID
join sysobjects so on so.name = 'J5C_Measures_Sys'
join syscolumns sc on so.id = sc.id
join [J5C_MeasureNamesV2_Sys] v on v.Score_field_id = sc.name
where so.type = 'u' and sc.name = 'score_14' and a.score_14 is not null
AND A.STUDENTID IS NOT NULL AND A.MEASUREDATE IS NOT NULL AND B.MEASURENAME IS NOT NULL
AND NOT EXISTS(SELECT NULL
FROM J5C_MasterMeasures x
WHERE x.studentid = a.studentid
AND x.measuredate = a.measuredate
AND x.measureid = B.measurename +' '+ B.LabelName)
group by a.studentid, a.measuredate, B.measurename, B.LabelName, A.score_14
The NOT EXISTS will filter out already existing data.

You should double-check your HAVING test. Make sure you are only including the column(s) that comprises the PK in your GROUP BY clause.

Related

mysql: duplicate column name on join with subquery

I've searched a lot but I still don't get it.
Here's my sample code
SELECT sp.testno, sp.companyid, st.*
FROM sponsor AS sp
LEFT JOIN
(
SELECT a.sponsorempno, (CASE WHEN t.companyid IS NULL OR t.companyid = '' THEN'aa' ELSE t.companyid END) agncy, a.controlno, a.tnpl, t.*
FROM applicant AS a
LEFT JOIN
test AS t
ON a.controlno = t.controlno
) AS st
ON sp.testno = st.testno
I still returns an error:
#1060 - Duplicate column name 'controlno'
Can somebody tell me what's wrong with the code?
In the subselect of your join, you are selecting a.controlno and by t.* t.controlno.
You should provide an alias for one of the selected columns. In your case a.controlno. This is necessary, because the table aliases of the inner select are lost, when accessing it from the outer one.
The statement below should work, if there aren't any other duplicate column names in test and the set of used columns from applicant.
SELECT sp.testno, sp.companyid, st.*
FROM sponsor AS sp
LEFT JOIN
(
SELECT a.sponsorempno, (CASE WHENt.companyid IS NULL OR t.companyid = '' THEN'aa' ELSE t.companyid END) agncy, a.controlno as a_controlno, a.tnpl, t.*
FROM applicant AS a
LEFT JOIN
test AS t
ON a.controlno = t.controlno
) AS st
ON sp.testno = st.testno

MySQL query with multiple full joins

I have a query which gets all the jobs from a database, some jobs don't have a languagepair and I need to get those too while still getting the languagepair information for jobs witch have a languagepair, I understand this is done with a full join but full joins do not exist in mySQL, I read about it and I need to do some sort of UNION.
If I get NULLS as source & target for jobs that do not have a languagepair it is good.
This is the query I have at the moment:
SELECT jobName, source.name AS source, target.name AS target FROM (
(SELECT jobs.name AS jobName, lp.sourceId, lp.targetId FROM jobs **JOIN languagePairs** lp
ON lp.id = jobs.languagePairId)
UNION
(SELECT jobs.name AS jobName, lp.sourceId, lp.targetId FROM collectiveJobs JOIN jobs ON jobs.id = collectiveJobs.jobId
**JOIN languagePairs lp** on jobs.languagePairId = lp.id
WHERE collectiveJobs.freelancerId = 1)
) AS jobs **JOIN languages** source ON source.id = sourceId **JOIN languages** target ON target.id = targetId;
I think but I am not sure the full joins need to happen at the bold joins. There also needs to be some sort of checking for null (I think) in the query.
Off course I could do this programmatically but it would be nice to have 1 query for it.
DB schema:
create table languages
(
id int auto_increment primary key,
name varchar(255) not null
)
create table languagePairs
(
id int auto_increment
primary key,
sourceId int not null,
targetId int not null,
constraint languagePair_sourceId_targetId_uindex
unique (sourceId, targetId),
constraint languagePair_language_id_fk_source
foreign key (sourceId) references languages (id),
constraint languagePair_language_id_fk_target
foreign key (targetId) references languages (id)
)
create table jobs
(
id int auto_increment
primary key,
name varchar(255) null,
freelancerId int null,
languagePairId int null,
constraint jobs_freelancers_id_fk
foreign key (freelancerId) references freelancers (id),
constraint jobs_languagePairs_id_fk
foreign key (languagePairId) references languagePairs (id)
)
create table collectiveJobs
(
id int auto_increment
primary key,
jobId int not null,
freelancerId int not null,
constraint collectiveJobs_freelancerId_jobId_uindex
unique (freelancerId, jobId),
constraint collectiveJobs_freelancers_id_fk
foreign key (freelancerId) references freelancers (id),
constraint collectiveJobs_jobs_id_fk
foreign key (jobId) references jobs (id)
)
create table freelancers
(
id int auto_increment primary key
)
Sample data:
INSERT INTO datamundi.jobs (id, name, freelancerId, languagePairId) VALUES (1, 'Job 1', 1, 1);
INSERT INTO datamundi.jobs (id, name, freelancerId, languagePairId) VALUES (2, 'Job 2', 1, null);
If I execute the query only Job 1 gets shown.
MySQL version on development machine: mysql Ver 8.0.19 for Linux on x86_64 (MySQL Community Server - GPL)
MySQL version on production server: mysql Ver 8.0.17 for Linux on x86_64 (MySQL Community Server - GPL)
All help is truly appreciated.
I can't really delve into your specific example, but the good news is you are using MySQL 8.x. The workaround for a FULL OUTER JOIN between two tables (a and b) in MySQL is:
select * from a left join b on <predicate>
union
select * from a right join b on <predicate>
Now if you need to join complex selects instead of simple tables, them CTEs come to your rescue. For example, if the left side were a comple SELECT you would do:
with s as ( <complex-select-here> )
select * from s left join b on <predicate>
union
select * from s right join b on <predicate>
If both are complex SELECTs then:
with s as ( <complex-select-here> ),
t as ( <complex-select-here> )
select * from s left join t on <predicate>
union
select * from s right join t on <predicate>
No sweat.
This works with all LEFT joins, I am sorry, I should have tried first.
SELECT jobName, source.name AS source, target.name AS target FROM (
(SELECT jobs.name AS jobName, lp.sourceId, lp.targetId FROM jobs LEFT JOIN languagePairs lp
ON jobs.languagePairId = lp.id)
UNION
(SELECT jobs.name AS jobName, lp.sourceId, lp.targetId FROM collectiveJobs JOIN jobs ON jobs.id = collectiveJobs.jobId
LEFT JOIN languagePairs lp on jobs.languagePairId = lp.id
WHERE collectiveJobs.freelancerId = 1)
) AS jobs LEFT JOIN languages source ON source.id = sourceId LEFT JOIN languages target ON target.id = targetId;
Not sure why I taught I needed a FULL JOIN...

Nested SELECT is hanging my SQL statement

I'm running a statement that's selecting stock market data from three tables. The last part of the statement is running a SELECT max(date) on a table that contains rows of stock data that is dated. I need the last date for a chosen stock from this table (tbl_asxd_extended.date). The problem is the statement just hangs and I can't work out why.
If I separate the statements, up to the final SELECT and run them independently they run fine! They just don't play well together when combined.
I'm not sure how to troubleshoot this one.
SELECT tbl_asxd_extended.close, tbl_asxd_extended.mcapintra, tbl_asxco.industry, tbl_asxco.company, tbl_watchlist.*
FROM tbl_watchlist
INNER JOIN tbl_asxco ON tbl_asxco.symbol = tbl_watchlist.symbol
INNER JOIN tbl_asxd_extended ON tbl_asxd_extended.symbol = tbl_watchlist.symbol
WHERE user_email='testuser#test.com'
AND tbl_asxd_extended.date =
(SELECT max(tbl_asxd_extended.date) FROM tbl_asxd_extended
WHERE tbl_watchlist.symbol = tbl_asxd_extended.symbol)
Here is an 'EXPLAIN' of the statement
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY tbl_watchlist ALL NULL NULL NULL NULL 9 Using where
1 PRIMARY tbl_asxco eq_ref symbol_2,symbol symbol_2 32 func 1 Using where
1 PRIMARY tbl_asxd_extended ALL NULL NULL NULL NULL 2195 Using where; Using join buffer
2 DEPENDENT SUBQUERY tbl_asxd_extended ALL NULL NULL NULL NULL 2195 Using where
SELECT tbl_asxd_extended.close, tbl_asxd_extended.mcapintra, tbl_asxco.industry, tbl_asxco.company, tbl_watchlist.*
FROM tbl_watchlist
INNER JOIN tbl_asxco ON tbl_asxco.symbol = tbl_watchlist.symbol
INNER JOIN tbl_asxd_extended ON tbl_asxd_extended.symbol = tbl_watchlist.symbol
WHERE user_email='testuser#test.com'
AND tbl_asxd_extended.date =
(SELECT MAX(tbl_asxd_extended.date) FROM tbl_asxd_extended
WHERE tbl_watchlist.symbol = tbl_asxd_extended.symbol)
this will solve your problem as you're using the max() instead of MAX();
And if possible execute last select query first and store it in any variable say result and just assign result variable to the AND part condition matching
An uncorrelated subquery usually outperforms a correlated one:
SELECT e.close
, e.mcapintra
, s.industry
, s.company
, w.*
FROM tbl_watchlist w
JOIN tbl_asxco a
ON a.symbol = w.symbol
JOIN tbl_asxd_extended e
ON e.symbol = w.symbol
JOIN
( SELECT symbol
, MAX(date) date
FROM tbl_asxd_extended
GROUP
BY symbol
) x
ON x.symbol = e.symbol
AND x.date = e.date
WHERE user_email = 'testuser#test.com'
Further performance improvements may be gained by providing the EXPLAIN for the above together with CREATE TABLE statements for ALL relevant tables.

Update with subquery in MySql

I've two tables:
work (work_id (AI, PK), sent_date, received_date, visit_date)
history_work(id_history_work (AI, PK), work_id (FK), sent_date, reseived_date, visit_date)
Relationship shoud be 1->n.
I want to update work table so sent_date, received_date and visit_date shoud have the values of last inserted record in history_work table (last id_history value) with same work_id value.
You can do this by using join. Join once to the history table. Join a second time to get the maximum id (which is presumably the most recent insertion).
update work w join
history h
on w.work_id = h.work_id join
(select work_id, max(id_history_work) as maxihw
from history
group by work_id
) hw
on hw.maxihw = h.id_work_history
set w.sent_date = h.sent_date,
w.received_date = h.received_date,
w.visit_date = h.visit_date;

MySQL alternative to on duplicate key update

I'm trying to pull from multiple tables to insert into a table to create role assignments in moodle's database based on the categories that are created but I need it to update on duplicate key but I cant use ON DUPLICATE KEY UPDATE because the fields im trying to match on role id, context id, and user id are not primary keys in the mdl_role_assignments table.
insert into vclassmoodle.mdl_role_assignments (roleid,contextid,userid,timemodified,modifierid,itemid,sortorder) select
mdl_role.id as roleid, mdl_context.id as contextid, mdl_user.id as userid, unix_timestamp() as timemodified, 3 as modifierid, 0 as itemid, 0 as sortorder
from
mdl_context
left join
mdl_course_categories ON mdl_context.instanceid = mdl_course_categories.id
left join
mdl_user ON mdl_course_categories.idnumber = mdl_user.idnumber
join
mdl_role ON mdl_role.shortname = 'manager'
where mdl_context.contextlevel = 40 and mdl_course_categories.depth > 1
Let me know if I need to clarify on anything
Thanks
Just been having a look at the function role_assign() in /lib/accesslib.php
If there is a duplicate then it doesn't update, so you could just ignore duplicates.
Although you should really use the role_assign() function rather than insert data directly. In case the role assignment changes in the future, but also because it triggers a role_assigned event which might be used elsewhere.
Still use your query but ignore existing records and create a loop to call role_assign(), something like this
SELECT mdl_role.id as roleid,
mdl_context.id as contextid,
mdl_user.id as userid
FROM mdl_context
JOIN mdl_course_categories ON mdl_context.instanceid = mdl_course_categories.id
JOIN mdl_user ON mdl_course_categories.idnumber = mdl_user.idnumber
JOIN mdl_role ON mdl_role.shortname = 'manager'
WHERE mdl_context.contextlevel = 40
AND mdl_course_categories.depth > 1
AND NOT EXISTS (
SELECT mdl_role_assignments.id
FROM mdl_role_assignments
WHERE mdl_role_assignments.roleid = mdl_role.id
AND mdl_role_assignments.contextid = mdl_context.id
AND mdl_role_assignments.userid = mdl_user.id
AND mdl_role_assignments.itemid = 0
AND mdl_role_assignments.component = '')
Note that a duplicate is a combination of roleid, userid and contextid but also component and itemid. So component = '' needs to be checked too.