Replace t1 col with t2 col when [condition] + different datatype - sql

I have this situation: deceduti(sigla,denominazione_provincia) , province(sigla_provincia,denominazione_provincia)
where sigla is a NUMBER and sigla_provincia is a CHAR(2).
I want to set deceduti.sigla=province.sigla_provincia where deceduti.denominazione_provincia=province.denominazione_provincia.
I have tried different ways but no one worked. I'd appreciate to understand how can i do this with join operator. There are my tries:
MERGE INTO DECEDUTI -- error with the datatypes
USING PROVINCE
ON ( DECEDUTI.DENOMINAZIONE_PROVINCIA = PROVINCE.DENOMINAZIONE_PROVINCIA )
WHEN MATCHED THEN
UPDATE SET DECEDUTI.Sigla = PROVINCE.sigla_provincia;
/
UPDATE -- oracle doesnt have the on update+policy react
(SELECT DECEDUTI.Sigla as OLD, to_number(PROVINCE.Sigla_provincia) as NEW
FROM DECEDUTI
INNER JOIN PROVINCE
ON DECEDUTI.DENOMINAZIONE_PROVINCIA = PROVINCE. DENOMINAZIONE_PROVINCIA
) t
SET t.OLD = t.NEW
/
UPDATE deceduti T
SET T.sigla =
(SELECT distinct sigla_provincia
FROM PROVINCE A
WHERE A.denominazione_provincia = T.denominazione_provincia);

This is what you have:
create table deceduti
(sigla number,
denominazione_provincia unknown_datatype
);
create table province
(sigla_provincia char(2),
denominazione_provincia unknown_datatype
);
Data stored within looks like this (according to datatypes you mentioned):
DECEDUTI:
sigla denominazione_provincia
----- -----------------------
25 1000
null 2000
PROVINCE:
sigla_provincia denominazione_provincia
--------------- -----------------------
AB 1000
X1 2000
As tables are joined on denominazione_provincia, you'd like to - for its value = 1000 - put AB into the deceduti.sigla column. How do you plan to put AB into a NUMBER datatype column? That won't work.
The simplest option is to
alter table deceduti modify sigla (char(2));
but only if sigla column is empty; shouldn't be problem to set it to NULL as it is supposed to accept a new value anyway. If you want to keep some "old" values, then it gets more complicated (creating a new column, store current sigla value in there, empty sigla, change its datatype, move data back, drop new column).
Other options are more complicated, e.g.
make sure that all sigla_provincia values are numeric so that to_number(sigla_provincia) returns a number
update deceduti.sigla values only for denominazione_provincias whose pair in province contains number in sigla_provincia

Related

Dynamically Updating Columns with new Data

I am handling an SQL table with over 10K+ Values, essentially it controls updating the status of a production station over the day. Currently the SQL server will report a new message at the current time stamp - ergo a new entry can be generated for the same part hundreds of times a day whilst only having the column "Production_Status" and "TimeStamp" changed. I want to create a new table that selects unique part names then have two other columns that control bringing up the LATEST entry for THAT part.
I have currently selected the data - reordered it so the latest timestamp is first on the list. I am currently trying to do this dynamic table but I am new to sql.
select dateTimeStamp,partNumber,lineStatus
from tblPLCData
where lineStatus like '_ Zone %' or lineStatus = 'Production'
order by dateTimeStamp desc;
The Expected results should be a NewTable with the row count being based off how many parts are in our total production facility - this column will be static - then two other columns that will check Originaltable for the latest status and timestamp and update the two other columns in the newTable.
I don't need help with the table creation but more the logic that surrounds the updating of rows based off of another table.
Much Appreciated.
It looks like you could take advantage of a sub join that finds the MAX statusDate for each partNumber, then joins back to itself so that you can get the corresponding lineStatus value that corresponds to the record with the max date. I just have you inserting/updating a temp table but this can be the general approach you could take.
-- New table that might already exist in your db, I am creating one here
declare #NewTable(
partNumber int,
lineStatus varchar(max),
last_update datetime
)
-- To initially set up your table or to update your table later with new part numbers that were not added before
insert into #NewTable
select tpd.partNumber, tpd.lineStatus, tpd.lineStatusdate
from tblPLCData tpd
join (
select partNumber, MAX(lineStatusdate) lineStatusDateMax
from tblPLCData
group by partNumber
) maxStatusDate on tpd.partNumber = maxStatusDate.partNumber
and tpd.lineStatusdate = maxStatusDate.lineStatusDateMax
left join #NewTable nt on tbd.partNumber = nt.partNumber
where tpd.lineStatus like '_ Zone %' or tpd.lineStatus = 'Production' and nt.partNumber is null
-- To update your table whenever you deem it necessary to refresh it. I try to avoid triggers in my dbs
update nt set nt.lineStatus = tpd.lineStatus, nt.lineStatusdate = tpd.lineStatusDate
from tblPLCData tpd
join (
select partNumber, MAX(lineStatusdate) lineStatusDateMax
from tblPLCData
group by partNumber
) maxStatusDate on tpd.partNumber = maxStatusDate.partNumber
and tpd.lineStatusdate = maxStatusDate.lineStatusDateMax
join #NewTable nt on tbd.partNumber = nt.partNumber
where tpd.lineStatus like '_ Zone %' or tpd.lineStatus = 'Production'

set value column where id is in the other table

I am trying to modify a table of my taking the id of another table being that this other table is with another column, taking only if the data of that column is 2, take the ID of that column and use in the one that I want to change, example:
UPDATE QuestData SET RepeatFinish = 100000
WHERE QuestID =
(
SELECT * FROM Quest WHERE QuestID = 2
);
But QuestData have so much data and table Quest too, how can i make this?
UPDATE QuestData SET RepeatFinish = 100000
WHERE QuestID in
(
SELECT id FROM Quest WHERE QuestID = 2
);
Change the id in select by yours. when using the in with an other request the select must return a one field that will be used in IN, you are using * so we don't know with what we should compare the QuestID

Fill one column with data from one column in another table (randomly)

Let's say I have a table Geometry and another table Customer like this:
Geometry      Customer
City        ID    Location
----         --    --------
Berlin       1    (null)
Paris        2    (null)
London
Now I'd like to fill the column Location with data from the column City. "Randomly" would be nice but it doesn't matter at all.
I've tried
update Customer set Location = (select City from Geometry where rownum < 3);
but still getting this error: single-row subquery returns more than one row
UPDATE: I'd like to fill the whole column Location with one update statement. I'm using ORACLE. The result should look like this:
Customer
ID     Location
--      -------
1      Berlin
2      London
Does someone have any better idea?
Thank you very much!
SQL Server:
UPDATE
Customer
SET
Location = (SELECT TOP 1 City FROM Geometry ORDER BY NEWID());
Since you just want it to pick one record at "random", you need to specify the correct number of rows:
update Customer set Location = (select City from Geometry where rownum = 1);
But note that since the subquery is not correlated to the Customer at all, the subquery may be optimised to only run once, and the same (randomly chosen) City will probably be used to update all Locations.
I would do the following, create a trigger on customer:
CREATE OR REPLACE TRIGGER customer_location
BEFORE INSERT OR UPDATE OF location ON customer
FOR EACH ROW
WHEN (NVL(new.location, 'X') = 'X')
BEGIN
SELECT city INTO :new.location
FROM (
SELECT city FROM geometry
ORDER BY DBMS_RANDOM.VALUE
) WHERE rownum = 1;
END;
/
UPDATE customer SET location = 'X';
This will update the customers table with a matching, "random" location. The trigger will also produce a new "random" location when a record is INSERTed into customers - as long as the location to be inserted is 'X' or NULL. (This wouldn't be wise if you actually have a location X - plug in some other value there!)
It is very possible to write a stored procedure to do the same thing, but you would have to create a cursor to loop over all the rows of customers where location is NULL.
update customer set location =
(select city from (
select distinct c.id, g.city, dbms_random.value from customer c, geometry g
order by value, city, id
) randoms where randoms.id = customer.id and rownum=1);
distinct necessary if there were two equal random values for one id

Insert data from one table to other using select statement and avoid duplicate data

Database: Oracle
I want to insert data from table 1 to table 2 but the catch is, primary key of table 2 is the combination of first 4 letters and last 4 numbers of the primary key of table 1.
For example:
Table 1 - primary key : abcd12349887/abcd22339887/abcder019987
In this case even if the primary key of table 1 is different, but when I extract the 1st 4 and last 4 chars, the output will be same abcd9887
So, when I use select to insert data, I get error of duplicate PK in table 2.
What I want is if the data of the PK is already present then don't add that record.
Here's my complete stored procedure:
INSERT INTO CPIPRODUCTFAMILIE
(productfamilieid, rapport, mesh, mesh_uitbreiding, productlabelid)
(SELECT DISTINCT (CONCAT(SUBSTR(p.productnummer,1,4),SUBSTR(p.productnummer,8,4)))
productnummer,
ps.rapport, ps.mesh, ps.mesh_uitbreiding, ps.productlabelid
FROM productspecificatie ps, productgroep pg,
product p left join cpiproductfamilie cpf
on (CONCAT(SUBSTR(p.productnummer,1,4),SUBSTR(p.productnummer,8,4))) = cpf.productfamilieid
WHERE p.productnummer = ps.productnummer
AND p.productgroepid = pg.productgroepid
AND cpf.productfamilieid IS NULL
AND pg.productietype = 'P'
**AND p.ROWID IN (SELECT MAX(ROWID) FROM product
GROUP BY (CONCAT(SUBSTR(productnummer,1,4),SUBSTR(productnummer,8,4))))**
AND (CONCAT(SUBSTR(p.productnummer,1,2),SUBSTR(p.productnummer,8,4))) not in
(select productfamilieid from cpiproductfamilie));
The highlighted section seems to be wrong, and because of this the data is not picking up.
Please help
Try using this.
p.productnummer IN (SELECT MAX(productnummer) FROM product
GROUP BY (CONCAT(SUBSTR(productnummer,1,4),SUBSTR(productnummer,8,4))))

Factor (string) to Numeric in PostgreSQL

Similar to this, is it possible to convert a String field to Numeric in PostgreSQL. For instance,
create table test (name text);
insert into test (name) values ('amy');
insert into test (name) values ('bob');
insert into test (name) values ('bob');
insert into test (name) values ('celia');
and add a field that is
name | num
-------+-----
amy | 1
bob | 2
bob | 2
celia | 3
The most effective "hash"-function of all is a serial primary key - giving you a unique number like you wished for in the question.
I also deal with duplicates in this demo:
CREATE TEMP TABLE string (
string_id serial PRIMARY KEY
,string text NOT NULL UNIQUE -- no dupes
,ct int NOT NULL DEFAULT 1 -- count instead of dupe rows
);
Then you would enter new strings like this:
(Data-modifying CTE requires PostgreSQL 9.1 or later.)
WITH x AS (SELECT 'abc'::text AS nu)
, y AS (
UPDATE string s
SET ct = ct + 1
FROM x
WHERE s.string = x.nu
RETURNING TRUE
)
INSERT INTO string (string)
SELECT nu
FROM x
WHERE NOT EXISTS (SELECT 1 FROM y);
If the string nu already exists, the count (ct) is increased by 1. If not, a new row is inserted, starting with a count of 1.
The UNIQUE also adds an index on the column string.string automatically, which leads to optimal performance for this query.
Add additional logic (triggers ?) for UPDATE / DELETE to make this bullet-proof - if needed.
Note, there is a super-tiny race condition here, if two concurrent transactions try to add the same string at the same moment in time. To be absolutely sure, you could use SERIALIZABLE transactions. More info and links under this this related question.
Live demo at sqlfiddle.
How 'bout a hash, such as md5, of name?
create table test (name text, hash text);
-- later
update test set hash = md5(name);
If you need to convert that md5 text to a number: Hashing a String to a Numeric Value in PostgresSQL
If they are all single characters, you could do this:
ALTER TABLE test ADD COLUMN num int;
UPDATE test SET num = ascii(name);
Though that would only return the character for the first letter if the string was more than a single character.
The exact case shown in your request can be produced with the dense_rank window function:
regress=# SELECT name, dense_rank() OVER (ORDER BY name) FROM test;
name | dense_rank
-------+------------
amy | 1
bob | 2
bob | 2
celia | 3
(4 rows)
so if you were adding a number for each row, you'd be able to do something like:
ALTER TABLE test ADD COLUMN some_num integer;
WITH gen(gen_name, gen_num) AS
(SELECT name, dense_rank() OVER (ORDER BY name) FROM test GROUP BY name)
UPDATE test SET some_num = gen_num FROM gen WHERE name = gen_name;
ALTER TABLE test ALTER COLUMN some_num SET NOT NULL;
however I think it's much more sensible to use a hash or to assign generated keys. I'm just showing that your example can be achieved.
The biggest problem with this approach is that inserting new data is a pain. It's a ranking (like your example shows) so if you INSERT INTO test (name) VALUES ('billy'); then the ranking changes.