Converting SQL Server script to PostgreSQL - sql-server

There is this script in the SQL Server that needs to be converted in PostgreSQL.
Here is the script:
UPDATE Categories_convert SET Categories_convert.ParentID =
Categories_convert_1.CategoryID
FROM Categories_convert LEFT OUTER JOIN Categories_convert AS
Categories_convert_1 ON Categories_convert.MainGroupID_FK =
Categories_convert_1.MainGroupID
WHERE (((Categories_convert.Level)=2));
Then I tried to convert it to postgres. Here is the script:
UPDATE x_tmp_categories_convert SET orig.parentid =
cat2.categoryid
FROM x_tmp_categories_convert LEFT OUTER JOIN x_tmp_categories_convert AS
cat2 ON x_tmp_categories_convert.maingroupid_fk =
x_tmp_categories_convert.maingroupid
WHERE (((cat.level)=2));
Note that I have already created the table Categories_convert of SQLServer to Postgresql and renamed it to x_tmp_categories_convert .
All the fields in postgresql is in lowercase.
Now the problem is when i execute the converted script to postgresql, an error will occur:
ERROR: table name "x_tmp_categories_convert" specified more than once
SQL state: 42712
What I do wrong in the conversion?
UPDATE:
I have tried #a_horse_with_no_name 's answer but it didn't update the records at all. The parentid field is still empty. It is supposed to map all the parentid of that categor based on its maingoupid_fk.
Below is a snapshot of the records after executing the suggested script.
I have opted out the name for disclosure reasons.
Records snapshot link
UPDATE v2:
I am using php to migrate the data so pardon me for the variables used.
Here are the 2 insert statements used before the questioned update script is executed:
INSERT INTO x_tmp_categories_convert(maingroupid,name,vendorid,level,parentid)
VALUES ($id,'$mainGroup',$vendorID,1,Null);
INSERT INTO x_tmp_categories_convert(subgroupid,maingroupid_fk,name,vendorid,level)
VALUES ($id,'$mainGroupId','$subGroup',$vendorID,2);
Also, this is the table definition of the x_tmp_categories_convert table:
CREATE TABLE x_tmp_categories_convert
(
categoryid serial NOT NULL,
parentid double precision,
name character varying(255),
level double precision,
vendorid double precision,
maingroupid integer,
subgroupid integer,
maingroupid_fk integer,
pageid integer,
subgroupid_fk integer,
CONSTRAINT code_pk PRIMARY KEY (categoryid)
)
WITH (
OIDS=FALSE
);
UPDATE v3:
Already SOLVED by a_horse_with_no_name. Thank you

I have edited #a_horse_with_no_name 's answer and to make it work. I guess it is just a copy paste error.
Here is the final script:
UPDATE x_tmp_categories_convert
SET parentid = cat.categoryid
FROM x_tmp_categories_convert AS cat
WHERE x_tmp_categories_convert.maingroupid_fk = cat.maingroupid
AND x_tmp_categories_convert.level = 2;
For #a_horse_with_no_name, I am very grateful to you as I cannot come up with the solution without your help. +1 for you man. Thank you

Related

checking if the same row exists in the table or not [duplicate]

I've got a table with data named energydata
it has just three columns
(webmeterID, DateTime, kWh)
I have a new set of updated data in a table temp_energydata.
The DateTime and the webmeterID stay the same. But the kWh values need updating from temp_energydata table.
How do I write the T-SQL for this the correct way?
Assuming you want an actual SQL Server MERGE statement:
MERGE INTO dbo.energydata WITH (HOLDLOCK) AS target
USING dbo.temp_energydata AS source
ON target.webmeterID = source.webmeterID
AND target.DateTime = source.DateTime
WHEN MATCHED THEN
UPDATE SET target.kWh = source.kWh
WHEN NOT MATCHED BY TARGET THEN
INSERT (webmeterID, DateTime, kWh)
VALUES (source.webmeterID, source.DateTime, source.kWh);
If you also want to delete records in the target that aren't in the source:
MERGE INTO dbo.energydata WITH (HOLDLOCK) AS target
USING dbo.temp_energydata AS source
ON target.webmeterID = source.webmeterID
AND target.DateTime = source.DateTime
WHEN MATCHED THEN
UPDATE SET target.kWh = source.kWh
WHEN NOT MATCHED BY TARGET THEN
INSERT (webmeterID, DateTime, kWh)
VALUES (source.webmeterID, source.DateTime, source.kWh)
WHEN NOT MATCHED BY SOURCE THEN
DELETE;
Because this has become a bit more popular, I feel like I should expand this answer a bit with some caveats to be aware of.
First, there are several blogs which report concurrency issues with the MERGE statement in older versions of SQL Server. I do not know if this issue has ever been addressed in later editions. Either way, this can largely be worked around by specifying the HOLDLOCK or SERIALIZABLE lock hint:
MERGE INTO dbo.energydata WITH (HOLDLOCK) AS target
[...]
You can also accomplish the same thing with more restrictive transaction isolation levels.
There are several other known issues with MERGE. (Note that since Microsoft nuked Connect and didn't link issues in the old system to issues in the new system, these older issues are hard to track down. Thanks, Microsoft!) From what I can tell, most of them are not common problems or can be worked around with the same locking hints as above, but I haven't tested them.
As it is, even though I've never had any problems with the MERGE statement myself, I always use the WITH (HOLDLOCK) hint now, and I prefer to use the statement only in the most straightforward of cases.
I often used Bacon Bits great answer as I just can not memorize the syntax.
But I usually add a CTE as an addition to make the DELETE part more useful because very often you will want to apply the merge only to a part of the target table.
WITH target as (
SELECT * FROM dbo.energydate WHERE DateTime > GETDATE()
)
MERGE INTO target WITH (HOLDLOCK)
USING dbo.temp_energydata AS source
ON target.webmeterID = source.webmeterID
AND target.DateTime = source.DateTime
WHEN MATCHED THEN
UPDATE SET target.kWh = source.kWh
WHEN NOT MATCHED BY TARGET THEN
INSERT (webmeterID, DateTime, kWh)
VALUES (source.webmeterID, source.DateTime, source.kWh)
WHEN NOT MATCHED BY SOURCE THEN
DELETE
If you need just update your records in energydata based on data in temp_energydata, assuming that temp_enerydata doesn't contain any new records, then try this:
UPDATE e SET e.kWh = t.kWh
FROM energydata e INNER JOIN
temp_energydata t ON e.webmeterID = t.webmeterID AND
e.DateTime = t.DateTime
Here is working sqlfiddle
But if temp_energydata contains new records and you need to insert it to energydata preferably with one statement then you should definitely go with the answer that Bacon Bits gave.
UPDATE ed
SET ed.kWh = ted.kWh
FROM energydata ed
INNER JOIN temp_energydata ted ON ted.webmeterID = ed.webmeterID
Update energydata set energydata.kWh = temp.kWh
where energydata.webmeterID = (select webmeterID from temp_energydata as temp)
THE CORRECT WAY IS :
UPDATE test1
INNER JOIN test2 ON (test1.id = test2.id)
SET test1.data = test2.data

MSSQL Data type conversion

I have a pair of databases (one mssql and one oracle), ran by different teams. Some data are now being synchronized regularily by a stored procedure in the mssql table. This stored procedure is calling a very large
MERGE [mssqltable].[Mytable] as s
USING THEORACLETABLE.BLA as t
ON t.[R_ID] = s.[R_ID]
WHEN MATCHED THEN UPDATE SET [Field1] = s.[Field1], ..., [Brokenfield] = s.[BrokenField]
WHEN NOT MATCHED BY TARGET THEN
... another big statement
Field Brokenfield was a numeric one until today, and could take value NULL, 0, 1, .., 24
Now, the oracle team introduced a breaking change today for some reason, changed the type of the column to string and now has values NULL, "", "ALFA", "BRAVO"... in the column. Of course, the sync got broken.
What is the easiest way to fix the sync here? I (Mysql team lead, frontend expert but not so in databases) would usually apply one of our database expert guys here, but all of them are now ill, and the fix must go online today....
I thought of a stored procedure like CONVERT_BROKENFIELD_INT_TO_STRING or so, based on some switch-case, which could be called in that merge statement, but not sure how to do that.
Edit/Clarification:
What I need is a way to make a chunk of SQL code (stored procedure), taking an input of "ALFA" and returning 1, "BRAVO" -> 2, etc. and which can be reused, to avoid writing huge ifs in more then one place.
If you can not simplify the logic for correct values the way #RichardHansell desribed, you can create a crosswalk table for BrokenField to the correct values. Then you can use a common table expression or subquery with a left join to that crosswalk to use in the merge.
create table dbo.BrokenField_Crosswalk (
BrokenField varchar(32) not null primary key
, CorrectedValue int
);
insert into dbo.BrokenField_Crosswalk (BrokenField,CorrectedValue) values
('ALFA', 1)
, ('ALPHA', 1)
, ('BRAVO', 2)
...
go
And your code for the merge would look something like this:
;with cte as (
select o.R_ID
, o.Field1
, BrokenField = cast(isnull(c.CorrectedValue,o.BrokenField) as int)
....
from oracle_table.bla as o
left join dbo.BrokenField_Crosswalk as c
)
merge into [mssqltable].[Mytable] t
using cte as s
on t.[R_ID] = s.[R_ID]
when matched
then update set
[Field1] = s.[Field1]
, ...
, [Brokenfield] = s.[BrokenField]
when not matched by target
then
If they are using names with a letter at the start that goes in a sequence:
A = 1
B = 2
C = 3
etc.
Then you could do something like this:
MERGE [mssqltable].[Mytable] as s
USING THEORACLETABLE.BLA as t
ON t.[R_ID], 1)) - ASCII('A') + 1 = s.[R_ID]
WHEN MATCHED THEN UPDATE SET [Field1] = s.[Field1], ..., [Brokenfield] = s.[BrokenField]
WHEN NOT MATCHED BY TARGET THEN
... another big statement
Edit: but actually I re-read your question and you are talking about [Brokenfield] being the problem column, so my solution wouldn't work.
I don't really understand now, as it seems as though the MERGE statement is updating the oracle table with numbers, so surely you need the mapping to work the other way, i.e. 1 -> ALFA, 2 -> BETA, etc.?

Convert multiple SQL Server queries into one

I have a page that ask of users opinion about a topic. Their responses are then saved into a table. What I want to do is to check how many users selected an option 1,2,3 and 4.
What I have now are multiple T-SQL queries that run successfully but I believe there is a simplified version of the code I have written. I would be grateful if someone can simplify my queries into one single query. Thank you.
here is sample of data in the database table
enter image description here
$sql4 = "SELECT COUNT(CO) FROM GnAppItms WHERE CO='1' AND MountID='".$mountID."'";
$stmt4 = sqlsrv_query($conn2, $sql4);
$row4 = sqlsrv_fetch_array($stmt4);
$sql5="SELECT COUNT(CO) FROM GnAppItms WHERE CO='2' AND MountID='".$mountID."'";
$stmt5=sqlsrv_query($conn2,$sql5);
$row5=sqlsrv_fetch_array($stmt5);
$sql6="SELECT COUNT(CO) FROM GnAppItms WHERE CO='3' AND MountID='".$mountID."'";
$stmt6=sqlsrv_query($conn2,$sql6);
$row6=sqlsrv_fetch_array($stmt6);
$sql7="SELECT COUNT(CO) FROM GnAppItms WHERE CO='4' AND MountID='".$mountID."'";
$stmt7=sqlsrv_query($conn2,$sql7);
$row7=sqlsrv_fetch_array($stmt7);
You can do it by using group by in sql server
example :
create table a
(id int,
mountid nvarchar(100),
co int,
)
insert into a values (1,'aa',1)
insert into a values (2,'aa',2)
insert into a values (3,'aa',1)
insert into a values (4,'aa',2)
insert into a values (5,'aa',3)
Query
select co,count(co)as countofco from a
where mountid='aa'
group by
co
result
co countofco
1 2
2 2
3 1
Note : Beware of SQL injection when you are writing a sql query, so always use parametrized query. You can edit the above example code and make it as a parametrized query for preventing sql injection

SSIS Foreach Loop failure

I have created a lookup for a list of IDs and a subsequent Foreach loop to run an sql stmt for each ID.
My variable for catching the list of IDs is called MissingRecordIDs and is of type Object. In the Foreach container I map each value to a variable called RecordID of type Int32. No fancy scripts - I followed these instructions: https://www.simple-talk.com/sql/ssis/implementing-foreach-looping-logic-in-ssis-/ (without the file loading part - I am just running an SQL stmt).
It runs fine from within SSIS, but when I deploy it my Integration Services Catalogue in MSSQL it fails.
This is the error I get when running from SQL Mgt Studio:
I thought I could just put a Precendence Constraint after MissingRecordsIDs get filled to check for NULL and skip the Foreach loop if necessary - but I can't figure out how to check for NULL in an Object variable?
Here is the Variable declaration and Object getting enumerated:
And here is the Variable mapping:
The SQL stmt that is in 'Lookup missing Orders':
select distinct cast(od.order_id as int) as order_id
from invman_staging.staging.invman_OrderDetails_cdc od
LEFT OUTER JOIN invman_staging.staging.invman_Orders_cdc o
on o.order_id = od.order_id and o.BatchID = ?
where od.BatchID = ?
and o.order_id is null
and od.order_id is not null
In the current environment this query returns nothing - there are no missing Orders, so I don't want to go into the 'Foreach Order Loop' at all.
This is a known issue Microsoft is aware of: https://connect.microsoft.com/SQLServer/feedback/details/742282/ssis-2012-wont-assign-null-values-from-sql-queries-to-variables-of-type-string-inside-a-foreach-loop
I would suggest to add an ISNULL(RecordID, 0) to the query as well as set an expression to the component "Load missing Orders" in order to enable it only when RecordID != 0.
In my case it wasn't NULL causing the problem, the ID value which I loaded from database was stored as nvarchar(50), even if it was a integer, I attempted to use it as integer in SSIS and it kept giving me the same error message, this worked for me:
SELECT CAST(id as INT) FROM dbo.Table

Check for duplicate records in sql database using vb.net

Assuming my table consist of two columns ID and Name.
And assume I have my stored procedure working on vb.net that inserts rows into the database.
But my system needs to check if an ID entered in a textbox already exists in the database when ADD button is click.
CREATE PROCEDURE AddOfficeEquipmentProfile
(
#OE_ID varchar(11),
#OE_Category char(3) =NULL,
#OE_SubCategory char(3)= NULL,
#OE_Name varchar(35)=NULL,
#OE_User varchar(35)=NULL,
#OE_Brand varchar(15)=NULL,
#OE_Model varchar(35)=NULL,
#OE_Specs varchar(1000)=NULL,
#OE_SerialNo varchar(35)=NULL,
#OE_PropertyNo varchar(35)=NULL,
#OE_MacAddress varchar(100)=NULL,
#OE_Static_IP varchar(15)=NULL,
#OE_Vendor varchar(35)=NULL,
#OE_PurchaseDate smalldatetime,
#OE_WarrantyInclusiveYear int=NULL,
#OE_WarrantyStatus char(2)= NULL,
#OE_Status varchar(15)=NULL,
#OE_Dept_Code char(3)= NULL,
#OE_Location_Code char(8)= NULL,
#OE_Remarks varchar(1000)= NULL
)
AS
INSERT INTO tblOfficeEquipmentProfile (OE_ID, OE_Category, OE_SubCategory, OE_Name, OE_User, OE_Brand, OE_Model, OE_Specs, OE_SerialNo,
OE_PropertyNo, OE_MacAddress, OE_Static_IP, OE_Vendor, OE_PurchaseDate, OE_WarrantyInclusiveYear, OE_WarrantyStatus, OE_Status, OE_Dept_Code,
OE_Location_Code, OE_Remarks )
VALUES (#OE_ID, #OE_Category, #OE_SubCategory, #OE_Name, #OE_User, #OE_Brand, #OE_Model,
#OE_Specs, #OE_SerialNo, #OE_PropertyNo, #OE_MacAddress, #OE_Static_IP, #OE_Vendor, #OE_PurchaseDate, #OE_WarrantyInclusiveYear, #OE_WarrantyStatus,
#OE_Status, #OE_Dept_Code, #OE_Location_Code, #OE_Remarks)
GO
few things you can do
make ID column as primary key, when insert you will get exception if duplicated
You can use auto increment ID, then you don't need to check ID exit or not. database will handle that
If you can't do above, run select statement or stored procedure to check whether id exist or not.
If this is for SQL Server and you're using a stored procedure - just try something like this:
CREATE PROCEDURE AddOfficeEquipmentProfile
(
#OE_ID varchar(11),
..... all your other parameters here.....
)
AS
IF NOT EXISTS (SELECT * FROM dbo.tblOfficeEquipmentProfile WHERE OE_ID = #OE_ID)
INSERT INTO dbo.tblOfficeEquipmentProfile(.... list of columns.....)
VALUES (......list of values................)
Assuming that OE_ID is your primary key and will be unique. Just check if that #OE_ID doesn't exist yet, and if it doesn't - insert the data. If it exists - don't do anything.
Building on the answer from #marc_s. In order to show a message to the user in case there already is a row in the database with the same id, you can check the number of affected rows from the query execution result.
This assumes that the stored procedure only inserts the row if the id is not present and does not emit any errors/exceptions.
Using ADO.NET (with an existing command executing the stored procedure):
Dim affectedRows as Integer = command.ExecuteNonQuery()
If affectedRows = 0 Then
'Handle the error here
MessageBox.Show("There is already a Profile with the supplied id")
Else
'Insert was made
End If
Check the following article to create a SP finding duplicate rows in any table:
http://www.codeproject.com/Articles/157977/Remove-Duplicate-Rows-from-a-Table-in-SQL-Server

Resources