reshape data in plsql [closed] - plsql

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
I write a query which returns result like this :
but I should reshape result like below :
in fact, what I want is to track date and doc_no and qty for specific act_desc_, Can someone help me to rewrite query reshape in PL SQL? I would appreciate it if somebody can help me.

Since you've posted an image, i'll give you an example of how you can do it using some quick mock data, with SQL:
select ACT_DESC, MAX(EXCAVATION_QTY) as EXCAVATION_QTY, MAX(FORM_WORK_COMPLETION_QTY) as FORM_WORK_COMPLETION_QTY
from (
select QTY, ACT_DESC, TASK,
CASE WHEN TASK = 'EXCAVATION' THEN QTY ELSE NULL END as EXCAVATION_QTY,
CASE WHEN TASK = 'Form Work Completion' THEN QTY ELSE NULL END as FORM_WORK_COMPLETION_QTY
FROM
(
select 1 as QTY, 'a' as ACT_DESC, 'Excavation' as TASK
UNION ALL
select 2 as QTY, 'a' as ACT_DESC, 'Form Work Completion' as TASK
UNION ALL
select 11 as QTY, 'b' as ACT_DESC, 'Excavation' as TASK
UNION ALL
select 22 as QTY, 'b' as ACT_DESC, 'Form Work Completion' as TASK
) as t
) t1
GROUP BY ACT_DESC
It takes:
and converts it into:

/*
There are numerous complex queries that can be used, but here is a quick
and simple method that creates a base table first, then populates it after.
*/
--Step 1: Create empty base table with columns of the correct data type
CREATE TABLE BASE_TABLE AS
(
SELECT
ACT_DESC_,
QTY AS EXCAVATION_QTY,
APP_DATE AS EXCAVATION_APP_DATE,
DOC_NO AS EXCAVATION_DOC_NO,
QTY AS FORM_WORK_COMP_QTY,
APP_DATE AS FORM_WORK_COMP_APP_DATE,
DOC_NO AS FORM_WORK_COMP_DOC_NO,
FROM
SOURCE_TABLE --this is the original table, or the table from the first image
WHERE 0=1 --this clause condition is used to create an empty table
);
--Step 2: Insert into base table the unique ACT_DESC_ values
INSERT INTO BASE_TABLE
(
SELECT DISTINCT
ACT_DESC_
FROM
SOURCE_TABLE
);
--Using Merge statements
--Step 3: Update base table based on TASK_ = 'Excavation'
MERGE INTO BASE_TABLE TGT --Alias TGT short for TarGeT table
USING
(
SELECT * FROM SOURCE_TABLE
WHERE TASK_ = 'Excavaion'
) SRC --Alias SRC short for SouRCe table
ON ( TGT.ACT_DESC_ = SRC.ACT_DESC_ )
WHEN MATCHED THEN UPDATE
SET
TGT.EXCAVATION_QTY = SRC.QTY
TGT.EXCAVATION_APP_DATE = SRC.APP_DATE
TGT.EXCAVATION_DOC_NO = SRC.DOC_NO;
--Step 4: Update base table based on TASK_ = 'Form Work Completion'
MERGE INTO BASE_TABLE TGT --Alias TGT short for TarGeT table
USING
(
SELECT * FROM SOURCE_TABLE
WHERE TASK_ = 'Form Work Completion'
) SRC --Alias SRC short for SouRCe table
ON ( TGT.ACT_DESC_ = SRC.ACT_DESC_ )
WHEN MATCHED THEN UPDATE
SET
TGT.FORM_WORK_COMP_QTY = SRC.QTY
TGT.FORM_WORK_COMP_APP_DATE = SRC.APP_DATE
TGT.FORM_WORK_COMP_DOC_NO = SRC.DOC_NO;

Related

No more spool space in Teradata while trying Update

I'm trying to update a table with to many rows 388.000.
This is the query:
update DL_RG_ANALYTICS.SH_historico
from
(
SELECT
CAST((MAX_DIA - DIA_PAGO) AS INTEGER) AS DIAS_AL_CIERRE_1
FROM
(SELECT * FROM DL_RG_ANALYTICS.SH_historico A
LEFT JOIN
(SELECT ANO||MES AS ANO_MES, MAX(DIA) AS MAX_DIA FROM DL_RG_ANALYTICS.SH_CALENDARIO
GROUP BY 1) B
ON A.ANOMES = B.ANO_MES
) M) N
SET DIAS_AL_CIERRE = DIAS_AL_CIERRE_1;
Any help is apreciate.
This first thing I'd do is replace the SELECT * with only the columns you need. You can also remove the M derived table to make it easier to read:
UPDATE DL_RG_ANALYTICS.SH_historico
FROM (
SELECT CAST((MAX_DIA - DIA_PAGO) AS INTEGER) AS DIAS_AL_CIERRE_1
FROM DL_RG_ANALYTICS.SH_historico A
LEFT JOIN (
SELECT ANO || MES AS ANO_MES, MAX(DIA) AS MAX_DIA
FROM DL_RG_ANALYTICS.SH_CALENDARIO
GROUP BY 1
) B ON A.ANOMES = B.ANO_MES
) N
SET DIAS_AL_CIERRE = DIAS_AL_CIERRE_1;
What indexes are defined on the SH_CALENDARIO table? If there is a composite index of (ANO, MES) then you should re-write your LEFT JOIN sub-query to GROUP BY these two columns since you concatenate them together anyways. In general, you want to perform joins, GROUP BY and OLAP functions on indexes, so there will be less row re-distribution and they will run more efficiently.
Also, this query is updating all rows in the table with the same value. Is this intended, or do you want to include extra columns in your WHERE clause?

sqlite3 : Compare two cells from the same table

I have a table like this
CREATE TABLE "modules" ( `ID` INTEGER PRIMARY KEY AUTOINCREMENT, `name` TEXT, `version` TEXT, `deployID` INTEGER )
I want to be able to get all the rows from this database where the version is different between different deploy ID's. So, say I have a deploy ID 2 and a deployID 3 - my table might have 2000 odd modules for each of these deploys. I want to only get the rows where the name is the same but the version is different. Is this possible? I thought this query would do it but it seems to be returning me everything - twice!
SELECT a.* FROM modules a
INNER JOIN modules b
ON a.name == b.name
WHERE a.version != b.version
AND a.deployID = 3
AND b.deployID = 2
If you only care about deployids 2 and 3:
select m.*
from modules m
where
m.deployid in (2, 3)
and
exists (
select 1 from modules
where
name = m.name
and
deployID <> m.deployID
and
version <> m.version
)
If you don't need this condition:
deployID <> m.deployID
you can remove it.
I would probably use an exists query here:
SELECT m1.*
FROM modules m1
WHERE EXISTS (SELECT 1 FROM modules m2
WHERE m1.name = m2.name AND
m1.deployID <> m2.deployID AND m1.version <> m2.version);
Or maybe you want a more specific version:
SELECT m1.*
FROM modules m1
WHERE EXISTS (SELECT 1 FROM modules m2
WHERE m1.name = m2.name AND
LEAST(m1.deployID, m2.deployID) = 2 AND
GREATEST(m1.deployID, m2.deployID) = 3);

How to write an update statement for which number of updated columns is dynamic

I want to write an UPDATE statement in which the number of columns may vary for each run, based on the user choice.
Eg: DDL
CREATE TABLE "XX_MASK_REF"
( "TABLE_NAME" VARCHAR2(150 BYTE),
"COLUMN_NAME" VARCHAR2(150 BYTE),
"FLAG" VARCHAR2(2 CHAR) DEFAULT 'N'
);
A predefined set of table names and the respective column names are inserted into this table. The default value for the FLAG is set to 'N'.The user depends on his requirement will set FLAG to 'Y' which will vary for each run.
Sample data: Run 1
Table_Name Column_Name Flag
T1 C1 Y
T1 C2 Y
T1 C3 N
T2 C1 N
T2 C2 Y
Sample data: Run 2
Table_Name Column_Name Flag
T1 C1 Y
T1 C2 N
T1 C3 N
T2 C1 N
T2 C2 Y
I need an UPDATE statement which should run only for those columns to which the FLAG is set to Y group by table_name. From the dataset given above, In the Run 1, the No of columns for table T1 is 2 whereas in the Run 2 it is only one column that needs to be updated.
The update statement generated should be able to update all the records available in the table which means where clause is not required.
The values to be updated will be available in variables. I intended to use the TRANSLATE function through which the values will be passed to the column to the column to be updated.
I think something like this would help me
V_CHAR := 'Update' ||' ' ||V_TABLE_NAME||' ' || 'Set' ||' ' || V_COLUMN_NAME||'='||' ' || 'TRANSLATE('||V_COLUMN_NAME||', '1234567890','abcdefghijk')
Please advise me if i miss anything or unclear
As APC commented, your question is incomplete, It does not mention where the update values and the where clause for each table comes from.
If you are simply looking for generating update statements without where clauses to run manually after putting appropriate changes, you may use a simple select query like this
SELECT
'UPDATE '
|| table_name
|| ' SET '
||
LISTAGG(column_name
|| ' = :'
|| ROWNUM,',') WITHIN GROUP(
ORDER BY
column_name
)||';'
AS v_upd_set
FROM
xx_mask_ref
WHERE
flag = 'Y'
GROUP BY
table_name
This will display rows with bind arguments to be used for passing values while running them.
UPDATE_QUERY
-------------------------------
UPDATE T1 SET C1 = :1,C2 = :2;
UPDATE T2 SET C2 = :3;
If you don't want this solution, edit your question and provide us more details.

How to use a select query results in an update query?

I have two tables:
films(id,marksNum)
marks(id,film_id,mark)
I'd like to save number of marks for each film in films.marksNum.
My attempt is:
UPDATE films
SET marksNum=
(
SELECT count(id)
FROM marks
WHERE marks.film_id=films.id
GROUP BY marks.film_id
)
WHERE films.id=marks.film_id
but I'v got an error: no such column: marks.film_id
What am I doing wrong?
Thank you in advance!
Well, this is not an elegan solve but any way:
1)
CREATE TABLE `grp_marks` ( `film_id` INTEGER, `marks_num` INTEGER )
2)
INSERT INTO grp_marks(film_id,marks_num)
SELECT film_id,count(id)
FROM marks
GROUP BY film_id
3)
UPDATE films
SET marksNum=
(
SELECT marks_num
FROM grp_marks
WHERE (film_id=films.id)
)
WHERE EXISTS
(SELECT * FROM grp_marks WHERE grp_marks.film_id=films.id
)

Duplicate record by using with CTEs SQL Server 2008

I need to manage hierarchy data storing in my database. But I have a problem now. Please see my example
I have a table called COMMON.TASK_REL
My second table is called Common. task
I suppose need to sort the task_seq and return a result like below:
Task Name || Task_Seq
Item1 1
..Item1.2 1
...Item1.2.1 1
..Item1.1 2
Here is my query
--Common task SQL modify --
WITH ctLevel
AS
(
SELECT
C_TASK_ID AS Child
,P_Task_ID AS Parent
,common_task.TASK_SEQ AS taskOrder
,1 AS [Level]
,CAST(C_TASK_ID AS VARCHAR(MAX)) AS [Order]
,CAST (Replicate('.', 1) + common_task.TASK_NAME AS VARCHAR(25)) AS [Task_Name]
FROM
[COMMON.TASK_REL] as common_task_rel,
[COMMON.TASK] as common_task
WHERE common_task_rel.C_TASK_ID = common_task.TASK_ID
and common_task.[TASK_TYPE] = 'B' AND common_task.[MODULE_CODE] = 'LWRPT'
AND common_task.[STATUS] <> 'D'
UNION ALL
SELECT
C_TASK_ID AS Child
,P_Task_ID AS Parent
,common_task.TASK_SEQ AS taskOrder
,[Level] + 1 AS [Level]
,[Order] + '.' + CAST(C_TASK_ID AS VARCHAR(MAX)) AS [Order]
,CAST (Replicate('.', [Level] + 1) + common_task.TASK_NAME AS VARCHAR(25)) AS [Task_Name]
FROM [COMMON.TASK_REL] as common_task_rel
INNER JOIN ctLevel
ON ( P_Task_ID = Child ) , [COMMON.TASK] as common_task
WHERE common_task_rel.C_TASK_ID = common_task.TASK_ID
and common_task.[TASK_TYPE] = 'B' AND common_task.[MODULE_CODE] = 'LWRPT'
AND common_task.[STATUS] <> 'D'
)
-- Viewing Data
SELECT Child ,Parent ,taskOrder,Level,[Order],Task_Name
FROM ctLevel
GROUP BY Child ,Parent ,taskOrder,Level,[Order],Task_Name
order by [Order];
GO
But my result returns duplicated rows:
Anyone can help me correct my query? Thanks
I believe that your duplicates are coming from your root/anchor query. You should add the following to that query:
AND Task_Seq = 0
Basically, you only want the root to be set up as the beginning of the tree. 301|300 should not be picked up until the recursion section (the part after union all)
If that does not make sense, then I can repaste your query with the modification, but that seemed unnecessary for a small change.

Resources