sql loop to update data - mysql

Alright having a real tough time getting this working.
I have a database of employees. I am looking to better condition this data. By this i mean that many entries in the table have rows where the employee_sales_id is "?". Each employee can have multiple rows (each time they change jobs). For the same employee some entries have the employee_sales_id while others don't. I want to scan my table and update all "?" where the value can be picked out from another row. The Employee_id is unique for each employee.
DB Looks like this:
Employee_id | employee_sales_id | name
1234 | abc | Jim Smith
1234 | abc | Jim Smith
1234 | ? | Jim Smith
1234 | abc | Jim Smith
You see the 3rd row. I want to fix that and update it with abc. There are many employees so i cant do this manually. It has to be a sql script. Also would be great to have it process the data on insert.

UPDATE employee
JOIN
(SELECT employee_id,employee_sales_id FROM employee
GROUP BY employee_id
HAVING COUNT(employee_sales_id)>1
AND employee_sales_id!='?')x
ON employee.employee_id=x.employee_id
SET employee.employee_sales_id=x.employee_sales_id
WHERE employee.employee_sales_id='?'
SQL FIDDLE

Try
UPDATE employee
SET Employee_sales_id = ES.employee_sales_id
FROM
(SELECT DISTINCT Employee_id, employee_sales_Id
FROM Employee
where employee_sales_id <> '?') ES
INNER JOIN Employee E on E.Employee_id = ES.Employee_id
WHERE E.employee_sales_id = '?'
By the way I assume you have some sort of primary key and/or further attributes on the Employee table that we are not seeing in your question text otherwise you have duplicate rows in your table which are going to cause problems for you somewhere.
Update
Sorry, I'm using Sql Server. Did not see your question related to MySql.

Try:
update employees x join (select employee_id,
min(employee_sales_id) as good_id,
jobcode,
job_start_date
from employees
where employee_sales_id <> '?') y on x.employee_id = y.employee_id
and x.jobcode = y.jobcode
and x.job_start_date = y.job_start_date
set x.employee_sales_id = y.employee_sales_id
where x.employee_sales_id = '?'
If the employee_sales_id changes over time, then you would want the row with the ? to be populated with the last employee_sales_id value for the given employee_id where the job is the same as the current job and start date, I assume.

Related

Update data in database using SQL

I have one table in my database. Field of table are describe below.
ID | NAME | QUALIFICATION
1 | ABC | Phd
2 | XYZ | MBA
3 | ADS | MBA
Now my problem is related to update QUALIFICATION record. Suppose if I update record of QUALIFICATION, it should be append new value to existing value.
For example, I am going to update record of id=1. Now I update "QUALIFICATION" MCA then it should add MCA to the existing record Phd, separated with comma. Output will looks like below.
ID | NAME | QUALIFICATION
1 | ABC | Phd,MCA
2 | XYZ | MBA
3 | ADS | MBA
When "QUALIFICATION" is null then the update should not be add comma before MCA.
Thats a bad database design never store the data as comma separated string, this will make things messy in future.
You should think of normalizing the table something as for the student your table should look like
- id primary key auto_incremented
- name
- other columns related to student
Then another table as student_qualification
- id primary key auto_incremented
- id_student ( id from student table)
- qualification
So for each student you can add as many qualification as possible to this table and can easily do add/edit/delete data
You can later easily retrieve data using simple joining the table.
first u have to select your existing value of Qualification column
that u want to update
Using
select qualification from tb_name where id = 1
Using above query u will get your qualification column value
suppose in
$qulification
Now update that row using
update set tb_name set qualification = '".$qualification."."your new value" where id = 1
May you can try this
update employee set qualification = qualification || ',MCA' where id = 1
the above will work in oracle
EDIT:
Then you can have the case statement with it
update employee set qualification = case when qualification is null then
'MCA' else qualification || ',MCA' end where id = 1
You can test for NULL in the SET clause, and use concatenation to format the string appropriately.
update student
set qualification = concat(if (qualification is not null
, concat( qualification, ',')
, '' )
, 'MBA')
where id = 1;
Here is a working SQL Fiddle (which also demonstrates behaviour with a NULL qualification).
I agree with #Abhik that this is a bad design for this specific data, and normalization is the better approach for the use case you provide However, there are other use cases where doing this sort of update would be perfectly valid so the question is worthy of a proper answer..

How to rewrite a database subquery into a join?

I am trying rewrite this subquery into a join. I have read the other questions on SO but cant get this one working.
create table job (
emplid int,
effdt date,
title varchar(100),
primary key (emplid, effdt)
);
insert into job set emplid=1, effdt='2010-01-01', title='Programmer';
insert into job set emplid=1, effdt='2011-01-01', title='Programmer I';
insert into job set emplid=1, effdt='2012-01-01', title='Programmer II';
insert into job set emplid=2, effdt='2010-01-01', title='Analyst';
insert into job set emplid=2, effdt='2011-01-01', title='Analyst I';
insert into job set emplid=2, effdt='2012-01-01', title='Analyst II';
#Get each employees current job:
select *
from job a
where a.effdt=
(select max(b.effdt)
from job b
where b.emplid=a.emplid);
Results:
+--------+------------+---------------+
| emplid | effdt | title |
+--------+------------+---------------+
| 1 | 2012-01-01 | Programmer II |
| 2 | 2012-01-01 | Analyst II |
+--------+------------+---------------+
I would like to rewrite the query into a join, without a subquery. Is this possible?
Writing this as a join is perhaps a bit counterintuitive. The idea is to use a left outer join and include in the condition that b.effdt > a.effdt. This condition will match rows except when a.effdt takes on the maximum value. The query can then filter for these using a where:
select a.*
from job a left outer join
job b
on b.emplid = a.emplid and
b.effdt > a.effdt
where b.effdt is NULL;
Have you considered rewriting your schema?
If you are able to, it might be better to have a history or log table that has entries for when the effective date was changed, for which employee ID and what the previous title was. That way you would just query the actual table and get the results that you want.
This can be achieved by using triggers for whenever a row in the database is changed, then everything is handled at the database level.

Relating records with a relationship ID

I am trying to think through this programming project and I would like some input.
I have two tables with records that can relate to other records in the same table or records in the other table.
Lets call table 1 book and table 2 bank. Both tables have the same layout.
+----+----------+--------+--------+-----------------+
| id | tranDate | refNum | amount | relationship_id |
+----+----------+--------+--------+-----------------+
What I am trying to figure out is how I can get an incremented relationship_id for every relationship and if I can do it only using MySQL code.
For example: To find records that can relate to other records in the same database I look for all records with a common refNum and see if the sum of their amount equals zero. If it does I want to relate them.
UPDATE book
LEFT JOIN (SELECT refnum AS matchedref
FROM book
WHERE relationship_id IS NULL
GROUP BY refnum
HAVING Sum(amount) = 0) AS t1
ON refnum = matchedref
SET relationship_id = ???
WHERE matchedref IS NOT NULL;

Update subsequent duplicate field values in mysql

I have the following schema:
id | order_ref | description | price
Currently I have the following duplicate issue:
1 | 34567 | This is the description | 19.99
2 | 34567 | This is the description | 13.99
This was due to the data I was importing having the description for each item duplicated. Is there a way I can keep the first row, and then UPDATE the description on subsequent (up to approx 20 rows) to be 'AS ABOVE'?
1 | 34567 | This is the description | 19.99
2 | 34567 | - AS ABOVE - | 13.99
Thanks
-------UPDATED
UPDATE documents_orders_breakdown
SET `desc` = '- AS ABOVE -'
WHERE NOT id IN (SELECT id
FROM documents_orders_breakdown AS D
WHERE D.`desc` <> `desc`
ORDER BY D.id
LIMIT 1)
But this returns [Err] 1235 - This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'
--------UPDATED
UPDATE documents_orders_breakdown
SET `desc` = '- AS ABOVE -'
WHERE NOT id IN (SELECT MIN(id)
FROM documents_orders_breakdown AS t
WHERE t.`desc` = `desc`)
This now returns [Err] 1093 - You can't specify target table 'documents_orders_breakdown' for update in FROM clause
If this is a one-time thing, performance is not a big issue. You can run an UPDATE on all the records that are not returned by a SELECT with a LIMIT of 1.
UPDATE the_table
SET description = '- AS ABOVE -'
WHERE NOT id IN (SELECT id
FROM the_table t
WHERE t.description = the_table.description
ORDER BY t.id
LIMIT 1)
This query assumes you want to keep the description of the record whose id comes first (hence the ORDER BY).
Since you can't use LIMIT in subqueries, you can work around that by using the aggregate function MIN:
UPDATE the_table
SET description = '- AS ABOVE -'
WHERE NOT id IN (SELECT MIN(id)
FROM the_table t
WHERE t.description = the_table.description)
(Let's hope you can mix MIN and subqueries ;)
Apparently you can't SELECT from the table you're UPDATEing in MySQL. A workaround is to use an implicit temporary table. This is bad for performance, but, again, given this is a one-time thing, that's not a big concern.
UPDATE the_table
SET description = '- AS ABOVE -'
WHERE NOT id IN (SELECT m FROM (SELECT MIN(id) AS m
FROM the_table t
WHERE t.description = the_table.description) AS temp)
Relational datebases do not have a notion of subsequent. Records in a table are not in any particular order. If you do not specify an order in a SELECT query, you have to assume that the records are retrieved in an order that you do not expect.
The comment Oswald made about ordering (or lack thereof) of the rows is very important. You have no garuntee, period, that unsorted rows selected out of this table will be in the order you expect. This means that unless you specify the existing in table order every single time, things could be tagged 'AS ABOVE' even when this does not reflect reality. In addition, none of the provided solutions so far will deal with any out-of-sequence records properly.
Overall, this sounds more like a database design issue (specifically, a normalization problem), than a query issue.
Ideally, the descriptions would be extracted to some master datatable (along with the necessary ids). Then, the choice about the description to use is left to when the 'SELECT' runs. This has the added benefit of making the 'AS ABOVE' safe for changes in ordering.
So, assuming that each instance of the order_ref column should have a different description (barring the 'AS ABOVE' bit), the tables can be refactored as followed:
id | order_ref | price
=======================
1 | 34567 | 19.99
2 | 34567 | 13.99
and
order_ref_fk | description
==========================================
34567 | "This is the description"
At this point, you join to the description table normally. Displaying a different description is usually a display issue regardless, to be handled by whatever program you have outputting the rows to display (not directly in the database).
If you insist on doing this in-db, you could write the SELECT in this vein:
SELECT Orders.id, Orders.order_ref, Orders.price,
COALESCE(Dsc.description, 'AS ABOVE')
FROM Orders
LEFT JOIN (Description
JOIN (SELECT order_ref, MIN(id) AS id
FROM Orders
GROUP BY order_ref) Ord
ON Ord.order_ref = Description.order_ref_fk) Dsc
ON Dsc.order_ref_fk = Orders.order_ref
AND Dsc.id = Orders.id
ORDER BY Orders.order_ref, Orders.id

MySQL query for ALL search terms in index

I have a table of employees and their schedule, like so:
Emp_Name | Date
-------- -----
Smith | 08-01-2009
Jones | 08-01-2009
Goodman | 08-02-2009
Smith | 08-02-2009
Jones | 08-02-2009
Goodman | 08-03-2009
How would I write a query so that the results were only employee names of employees working on 08-02-2009 and 08-03-2009.
I'm getting caught up because all I can think of are ways to get the names for EITHER match, but I can't seem to find the right way to get the results for only the names that have matches for all search criteria across multiple rows.
So based on the example conditions, I should only get Goodman. But if I do a query like WHERE Date IS (list of dates) I would get Goodman and Smith and Jones, since Smith and Jones both work on 08-02-2009. If I try to do some kind of JOIN, I would not always end up with equal columns, since the number of days each employees works is variable.
I thought Union might be the way to go, but I wouldn't always know how may conditions someone was searching by.
Here's my first stab at it, there's probably a more efficient way than using HAVING though...
SELECT Emp_Name,COUNT(DISTINCT date) as daycount
FROM employee
WHERE date IN ('08-01-2009', '08-02-2009')
GROUP BY Emp_Name
HAVING daycount=2
If you don't need a generic solution, you can use this:
select Emp_Name
from employee
where Emp_Name
in (select Emp_Name from employee where Date = '08-02-2009')
and in (select Emp_Name from employee where Date = '08-03-2009')
...