How to import a CSV file through a stored function? - postgresql

I Have sample CSV file which contains 10 records.
So I want to upload the CSV file Thru stored procedure.
Is it possible to do that way. This is my stored function.
FOR i IN 1..v_cnt LOOP
SELECT idx_date,file_path INTO v_idx_date,v_file_path FROM cloud10k.temp_idx_dates
WHERE is_updated IS FALSE LIMIT 1;
COPY cloud10k.temp_master_idx_new(header_section) FROM v_file_path;
DELETE FROM cloud10k.temp_master_idx_new WHERE header_section NOT ILIKE '%.txt%';
UPDATE cloud10k.temp_master_idx_new SET CIK = split_part( header_section,'|',1),
company_name = split_part( header_section,'|',2),
form_type = split_part( header_section,'|',3),
date_filed = split_part( header_section,'|',4)::DATE,
accession_number = replace(split_part(split_part( header_section,'|',5),'/',4),'.txt',''),
file_path = to_char(SUBSTRING(SPLIT_PART(v_file_path,'master.',2) FROM 1 FOR 8)::DATE,'YYYY')
||'/'||to_char(SUBSTRING(SPLIT_PART(v_file_path,'master.',2) FROM 1 FOR 8)::DATE,'MM')
||'/'||to_char(SUBSTRING(SPLIT_PART(v_file_path,'master.',2) FROM 1 FOR 8)::DATE,'DD')
||'/'||CONCAT_WS('.','master',SPLIT_PART(v_file_path,'master.',2) )
WHERE header_section ILIKE '%.txt%';
END LOOP;
But its not executing. Can someone suggest me how to do that..
Tanks,
Ramesh

Related

how to write a loop condition for to get the sub sub folders name in postgresql

I am trying to get the folders name which are in the subfolders.
Example:
folder_id folder_name parent_folder_id
1 F1 0
2 F2 1
3 F3 2
4 F4 3
Now I am trying to get the f4 name along with the parent folder name like :F1/F2/F3/F4
I am getting the parent_folder_id based on folder_id and wrote the loop condition,Here is my function.
for vrecord in (select parent_folder_id from public."VOfficeApp_filefolder"
where folder_id = ip_folder_id)
loop
return query
select (SELECT array_to_json(array_agg(row_to_json(b))) FROM
(select folder_name from public."VOfficeApp_filefolder"
where folder_id = v_id)b)as path;
end loop;
Aggregate functions do not build hierarchical results; for that you need a RECURSIVE CTE. Once the hierarchy is built you can then convert to json. The following function does that. The function takes the folder name you are interested in, after all it only used at the last minuet anyway to eliminate the rest of the hierarchy that as built.
create or replace function path_to_folder(target_folder_name text)
returns json
language sql
as $$
with recursive folder_path (id, folder_name, path) as
( select folder_id, folder_name,folder_name || '/'
from folders
where parent_folder_id = 0
union all
select f.folder_id, f.folder_name, fp.path || f.folder_name || '/'
from folders f
join folder_path fp on (f.parent_folder_id = fp.id)
)
, bjc (folder_name, path) as
( select folder_name, path
from folder_path
where folder_name = target_folder_name
) -- select * from bjc;
select json_agg(row_to_json((folder_name, path)))
from bjc
group by folder_name;
$$;
Note: the second cte bjc (Before Json Conversion) is probably not needed, but as I hate json (imho a complexity I'd rather not deal with). You could move the where clause from it into the json construction. But I always like to see results before converting.
Side Note:
Postgres 9.2 is obsolete. Having gone out of support in Nov, 2017. You seriously should update.

Merge - when matched then do nothing

I need to write a MERGE statement to insert data WHEN NOT MATCHED condition, WHEN MATCHED I'd like the query to do nothing but I've got to include this condition because I badly need to catch the source data from both conditions into my output table.
Here's my code:
MERGE dm_data_bps.dbo.akcja AS target
USING (
SELECT *
FROM #CEIDG
WHERE isnull(sp_id, '') <> ''
) AS source
ON target.ak_id = source.ceidg_ak_id
WHEN NOT MATCHED
THEN
INSERT (
ak_akt_id
,ak_sp_id
,ak_kolejnosc
,ak_interwal
,ak_zakonczono
,ak_pr_id
,ak_publiczna
)
VALUES (
1246
,sp_id
,0
,0
,getdate()
,5
,1
)
WHEN MATCHED
THEN
UPDATE
<DO NOTHING>
OUTPUT inserted.ak_id
,source.Firma
,source.AdresPocztyElektronicznej
,source.AdresStronyInternetowej
,source.IdentyfikatorWpisu
,source.DataRozpoczeciaWykonywaniaDzialalnosciGospodarczej
,source.DataZawieszeniaWykonywaniaDzialalnosciGospodarczej
,source.DataWznowieniaWykonywaniaDzialalnosciGospodarczej
,source.DataZaprzestaniaWykonywaniaDzialalnosciGospodarczej
,source.DataWykresleniaWpisuZRejestru
,source.MalzenskaWspolnoscMajatkowa
,source.SpolkiCywilneKtorychWspolnikiemJestPrzedsiebiorcaNIP
,source.SpolkiCywilneKtorychWspolnikiemJestPrzedsiebiorcaREGON
,source.Zakazy
,source.InformacjeDotyczaceUpadlosciPostepowaniaNaprawczego
,source.Sukcesja
,source.AdresGlownegoMiejscaWykonywaniaDzialalnosci
,source.AdresyDodatkowychMiejscWykonywaniaDzialalnosci
,source.AdresyDodatkowychMiejscWykonywaniaDzialalnosci2
,source.AdresDoDoreczen
,source.STATUS
INTO #ceidg_ak_id;
How can I accomplish my goal?
I'm not sure I'd bother with all of the overhead that comes with a MERGE statement. See Use Caution with SQL Server's MERGE Statement.
You can get everything you need with an explicit transaction.
BEGIN TRANSACTION;
UPDATE
target
SET
ak_akt_id = 1246
,ak_sp_id = sp_id
,ak_kolejnosc = 0
,ak_interwal = 0
,ak_zakonczono = GETDATE()
,ak_pr_id = 5
,ak_publiczna = 1
FROM
dm_data_bps.dbo.akcja AS target
JOIN
(SELECT * FROM #CEIDG WHERE sp_id <> '') AS source
ON
target.ak_id = source.ceidg_ak_id;
SELECT
Firma
,AdresPocztyElektronicznej
,AdresStronyInternetowej
,IdentyfikatorWpisu
,DataRozpoczeciaWykonywaniaDzialalnosciGospodarczej
,DataZawieszeniaWykonywaniaDzialalnosciGospodarczej
,DataWznowieniaWykonywaniaDzialalnosciGospodarczej
,DataZaprzestaniaWykonywaniaDzialalnosciGospodarczej
,DataWykresleniaWpisuZRejestru
,MalzenskaWspolnoscMajatkowa
,SpolkiCywilneKtorychWspolnikiemJestPrzedsiebiorcaNIP
,SpolkiCywilneKtorychWspolnikiemJestPrzedsiebiorcaREGON
,Zakazy
,InformacjeDotyczaceUpadlosciPostepowaniaNaprawczego
,Sukcesja
,AdresGlownegoMiejscaWykonywaniaDzialalnosci
,AdresyDodatkowychMiejscWykonywaniaDzialalnosci
,AdresyDodatkowychMiejscWykonywaniaDzialalnosci2
,AdresDoDoreczen
,STATUS
INTO
#ceidg_ak_id
FROM
#CEIDG
WHERE
sp_id <> '';
COMMIT TRANSACTION;

Postgres - limit number of rows COPY FROM

Is there a way to limit the Postgres COPY FROM syntax to only the first row? There doesn't seem to be an option listed in the documentation.
I know there's that functionality in SQL Server, see FIRSTROW AND LASTROW options below:
BULK INSERT sometable
FROM 'E:\filefromabove.txt
WITH
(
FIRSTROW = 2,
LASTROW = 4,
FIELDTERMINATOR= '|',
ROWTERMINATOR = '\n'
)
You could use the PROGRAM option to preprocess the file to read from the standard output.
To load only the first line use
Unix/Linux/Mac
COPY sometable from PROGRAM 'head -1 filefromabove.txt' ;
Windows
COPY sometable from PROGRAM 'set /p var= <filefromabove.txt && echo %var%' ;

understanding complex SP in DB2

I need to make changes to an SP which has a bunch of complex XML functions and what not
Declare ResultCsr2 Cursor For
WITH
MDI_BOM_COMP(PROD_ID,SITE_ID, xml ) AS (
SELECT TC401F.T41PID,TC401F.T41SID,
XMLSERIALIZE(
XMLAGG(
XMLELEMENT( NAME "MDI_BOM_COMP",
XMLFOREST(
trim(TC401F.T41CTY) AS COMPONENT_TYPE,
TC401F.T41LNO AS COMP_NUM,
trim(TC401F.T41CTO) AS CTRY_OF_ORIGIN,
trim(TC401F.T41DSC) AS DESCRIPTION,
TC401F.T41EFR AS EFFECTIVE_FROM,
TC401F.T41EFT AS EFFECTIVE_TO,
trim(TC401F.T41MID) AS MANUFACTURER_ID,
trim(TC401F.T41MOC) AS MANUFACTURER_ORG_CODE,
trim(TC401F.T41CNO) AS PROD_ID,
trim(TC401F.T41POC) AS PROD_ORG_CODE,
TC401F.T41QPR AS QTY_PER,
trim(TC401F.T41SBI) AS SUB_BOM_ID,
trim(TC401F.T41SBO) AS SUB_BOM_ORG_CODE, --ADB01
trim(TC401F.T41VID) AS SUPPLIER_ID,
trim(TC401F.T41SOC) AS SUPPLIER_ORG_CODE,
TC401F.T41UCT AS UNIT_COST
)
)
) AS CLOB(1M)
)
FROM TC401F TC401F
GROUP BY T41PID,T41SID
)
SELECT
RowNum, '<BOM_INBOUND>' ||
XMLSERIALIZE (
XMLELEMENT(NAME "INTEGRATION_MESSAGE_CONTROL",
XMLFOREST(
'FULL_UPDATE' as ACTION,
'POLARIS' as COMPANY_CODE,
TRIM(TC400F.T40OCD) as ORG_CODE,
'5' as PRIORITY,
'INBOUND_ENTITY_INTEGRATION' as MESSAGE_TYPE,
'POLARIS_INTEGRATION' as USERID,
'TA' as RECEIVER,
HEX(Generate_Unique()) as SOURCE_SYSTEM_TOKEN
),
XMLELEMENT(NAME "BUS_KEY",
XMLFOREST(
TRIM(TC400F.T40BID) as BOM_ID,
TRIM(TC400F.T40OCD) as ORG_CODE
)
)
) AS VARCHAR(1000)
)
|| '<MDI_BOM>' ||
XMLSERIALIZE (
XMLFOREST(
TRIM(TC400F.T40ATP) AS ASSEMBLY_TYPE,
TRIM(TC400F.T40BID) AS BOM_ID,
TRIM(TC400F.T40CCD) AS CURRENCY_CODE,
TC400F.T40DPC AS DIRECT_PROCESSING_COST,
TC400F.T40EFD AS EFFECTIVE_FROM,
TC400F.T40EFT AS EFFECTIVE_TO,
TRIM(TC400F.T40MID) AS MANUFACTURER_ID,
TRIM(TC400F.T40MOC) AS MANUFACTURER_ORG_CODE,
TRIM(TC400F.T40OCD) AS ORG_CODE,
TRIM(TC400F.T40PRF) AS PROD_FAMILY,
TRIM(TC400F.T40PID) AS PROD_ID,
TRIM(TC400F.T40POC) AS PROD_ORG_CODE,
TRIM(TC400F.T40ISA) AS IS_ACTIVE,
TRIM(TC400F.T40VID) AS SUPPLIER_ID,
TRIM(TC400F.T40SOC) AS SUPPLIER_ORG_CODE,
TRIM(TC400F.T40PSF) AS PROD_SUB_FAMILY,
CASE TRIM(TC400F.T40PML)
WHEN '' THEN TRIM(TC400F.T40PML)
ELSE TRIM(TC400F.T40PML) || '~' || TRIM(TC403F.T43MDD)
END AS PROD_MODEL
) AS VARCHAR(3000)
)
|| IFNULL(MBC.xml, '') ||
XMLSERIALIZE (
XMLFOREST(
XMLFOREST(
TRIM(TC400F.T40CCD) AS CURRENCY_CODE,
TC400F.T40PRI AS PRICE,
TRIM(TC400F.T40PTY) AS PRICE_TYPE
) AS MDI_BOM_PRICE,
XMLFOREST(
TRIM(TC400F.T40CCD) AS CURRENCY_CODE,
TRIM(TC400F.T40PRI) AS PRICE,
'TRANSACTION_VALUE' AS PRICE_TYPE
) AS MDI_BOM_PRICE,
XMLFOREST(
TRIM(TC400F.T40INA) AS INCLUDE_IN_AVERAGING
) AS MDI_BOM_IMPL_BOM_PROD_FAMILY_AUTOMOBILES
) AS VARCHAR(3000)
)
|| '</MDI_BOM>' ||
'</BOM_INBOUND>' XML
FROM (
SELECT
ROW_NUMBER() OVER (
ORDER BY T40STS
,T40SID
,T40BID
) AS RowNum
,t.*
FROM TC400F t
) TC400F
LEFT OUTER JOIN MDI_BOM_COMP MBC
ON TC400F.T40SID = MBC.SITE_ID
AND TC400F.T40PID = MBC.PROD_ID
LEFT OUTER JOIN TC403F TC403F
ON TC400F.T40PML <> ''
AND TC400F.T40PML = TC403F.T43MDL
WHERE TC400F.T40STS = '10'
AND TC400F.RowNUM BETWEEN
(P_STARTROW + (P_PAGENOS - 1) * P_NBROFRCDS)
AND (P_STARTROW + (P_PAGENOS - 1) * P_NBROFRCDS +
P_NBROFRCDS - 1);
Given above is a cursor declaration in the SP code which I am struggling to understand. The very first WITH itself seems to be mysterious. I have used it along with temporary table names but this is the first time, Im seeing something of this sort which seems to be an SP or UDF? Can someone please guide me on how to understand and make sense out of all this?
Adding further to the question, the actual requirement here is to arrange the data in the XML such a way that that those records which have TC401F.T41SBI field populated should appear in the beginning of the XML output..
This field is being selected as below in the code:
trim(TC401F.T41SBI) AS SUB_BOM_ID. If this field is non-blank, this should appear first in the XML and any records with this field value Blank should appear only after. What would be the best approach to do this? Using ORDER BY in any way does not really seem to help as the XML is actually created through some functions and ordering by does not affect how the items are arranged within the XML. One approach I could think of was using a where clause where TC401F.T41SBI <> '' first then append those records where TC401F.T41SBI = ''
Best I can do is help with the CTE.
WITH
MDI_BOM_COMP(PROD_ID,SITE_ID, xml ) AS (
SELECT TC401F.T41PID,TC401F.T41SID,
This just generates a table named MDI_BOM_COMP with three columns named PROD_ID, SITE_ID, and XML. The table will have one record for each PROD_ID, SITE_ID, and the contents of XML will be an XML snippet with all the components for that product and site.
Now the XML part can be a bit confusing, but if we break it down into it's scalar and aggregate components, we can make it a bit more understandable.
First ignore the grouping. so the CTE retrieves each row in TC401F. XMLELEMENT and XMLFORREST are scalar functions. XMLELEMENT creates a single XML element The tag is the first parameter, and the content of the element is the second in the above example. XMLFORREST is like a bunch of XMLELEMENTs concatenated together.
XMLSERIALIZE(
XMLAGG(
XMLELEMENT( NAME "MDI_BOM_COMP",
XMLFOREST(
trim(TC401F.T41CTY) AS COMPONENT_TYPE,
TC401F.T41LNO AS COMP_NUM,
trim(TC401F.T41CTO) AS CTRY_OF_ORIGIN,
trim(TC401F.T41DSC) AS DESCRIPTION,
TC401F.T41EFR AS EFFECTIVE_FROM,
TC401F.T41EFT AS EFFECTIVE_TO,
trim(TC401F.T41MID) AS MANUFACTURER_ID,
trim(TC401F.T41MOC) AS MANUFACTURER_ORG_CODE,
trim(TC401F.T41CNO) AS PROD_ID,
trim(TC401F.T41POC) AS PROD_ORG_CODE,
TC401F.T41QPR AS QTY_PER,
trim(TC401F.T41SBI) AS SUB_BOM_ID,
trim(TC401F.T41SBO) AS SUB_BOM_ORG_CODE, --ADB01
trim(TC401F.T41VID) AS SUPPLIER_ID,
trim(TC401F.T41SOC) AS SUPPLIER_ORG_CODE,
TC401F.T41UCT AS UNIT_COST
)
)
) AS CLOB(1M)
So in the example, for each row in the table, XMLFORREST creates a list of XML elements, one for each of COMPONENT_TYPE, COMP_NUM, CTRY_OF_ORIGIN, etc. These elements form the content of another XML element MDI_BOM_COMP which is created by XMLELEMENT.
Now for each row in the table we have selected PROD_ID, SITE_ID, and created some XML. Next we group by PROD_ID, and SITE_ID. The aggregation function XMLAGG will collect all the XML for each PROD_ID and SITE_ID, and concatenate it together.
Finally XMLSERIALIZE will convert the internal XML representation to the string format we all know and love ;)
I think I found the answer for my requirement. I had to add an order by field name after XMLELEMENT function

Using IndexOf and/Or Substring to parse data into separate columns

I am working on migrating data from one database to another for a hospital. In the old database, the doctor's specialty IDs are all in one column (swvar_specialties), each separated by commas. In the new database, each specialty ID will have it's own column (example: Specialty1_PrimaryID, Specialty2_PrimaryID, Specialty3_PrimaryID, etc). I am trying to export the data out of the old database and separate these into these separate columns. I know I can use indexof and substring to do this - I just need help with the syntax.
So this query:
Select swvar_specialties as Specialty1_PrimaryID
From PhysDirectory
might return results similar to 39,52,16. I need this query to display Specialty1_PrimaryID = 39, Specialty2_PrimaryID = 52, and Specialty3_PrimaryID = 16 in the results. Below is my query so far. I will eventually have a join to pull the specialty names from the specialties table. I just need to get this worked out first.
Select pd.ref as PrimaryID, pd.swvar_name_first as FirstName, pd.swvar_name_middle as MiddleName,
pd.swvar_name_last as LastName, pd.swvar_name_suffix + ' ' + pd.swvar_name_degree as NameSuffix,
pd.swvar_birthdate as DateOfBirth,pd.swvar_notes as AdditionalInformation, 'images/' + '' + pd.swvar_photo as ImageURL,
pd.swvar_philosophy as PhilosophyOfCare, pd.swvar_gender as Gender, pd.swvar_specialties as Specialty1_PrimaryID, pd.swvar_languages as Language1_Name
From PhysDirectory as pd
The article Split function equivalent in T-SQL? provides some details on how to use a split function to split a comma-delimited string.
By modifying the table-valued function in presented in this article to provide an identity column we can target a specific row such as Specialty1_PrimaryID:
/*
Splits string into parts delimitered with specified character.
*/
CREATE FUNCTION [dbo].[SDF_SplitString]
(
#sString nvarchar(2048),
#cDelimiter nchar(1)
)
RETURNS #tParts TABLE (id bigint IDENTITY, part nvarchar(2048) )
AS
BEGIN
if #sString is null return
declare #iStart int,
#iPos int
if substring( #sString, 1, 1 ) = #cDelimiter
begin
set #iStart = 2
insert into #tParts
values( null )
end
else
set #iStart = 1
while 1=1
begin
set #iPos = charindex( #cDelimiter, #sString, #iStart )
if #iPos = 0
set #iPos = len( #sString )+1
if #iPos - #iStart > 0
insert into #tParts
values ( substring( #sString, #iStart, #iPos-#iStart ))
else
insert into #tParts
values( null )
set #iStart = #iPos+1
if #iStart > len( #sString )
break
end
RETURN
END
Your query can the utilise this split function as follows:
Select
pd.ref as PrimaryID,
pd.swvar_name_first as FirstName,
pd.swvar_name_middle as MiddleName,
pd.swvar_name_last as LastName,
pd.swvar_name_suffix + ' ' + pd.swvar_name_degree as LastName,
pd.swvar_birthdate as DateOfBirth,pd.swvar_notes as AdditionalInformation,
'images/' + '' + pd.swvar_photo as ImageURL,
pd.swvar_philosophy as PhilosophyOfCare, pd.swvar_gender as Gender,
(Select part from SDF_SplitString(pd.swvar_specialties, ',') where id=1) as Specialty1_PrimaryID,
(Select part from SDF_SplitString(pd.swvar_specialties, ',') where id=2) as Specialty2_PrimaryID,
pd.swvar_languages as Language1_Name
From PhysDirectory as pd