Extract Oracle Pivot table to CSV file - sql

I have a created a pivot table in Oracle in order to extract data from a rather large and complex table to try and use the data to merge with some letters
Ideally what I need to do is to then export the data to a CSV file for transfer to the hybrid mail solution we have here.
I am struggling with the syntax of how the export to the CSV file might work in this case since some of the columns are created as part of the pivot process.
I had also thought of creating a temporary table using the "create global temporary table as select * from - and then the query for the pivot table. However trying this and then trying to select from the table created by the pivot leads to another complication as I am unable to query the dynamically created column as it is surrounded by '' single quotes.
Any ideas / comments suggestions gratefully received
The query to create the PIVOT table looks like this
select *
from
(
select
BAA_QSA_ID ,
BAA_ASM_ID ,
BAA_SUBJECT_ID ,
PER_FIRST_NAMES ||' '|| per_surname as per_name,
case
when BAA_QST_DESC = 'Name of Person or Organisation of contact and their contact details' then 'PER_ORG_OF_CONTACT' else BAA_QST_CODE end as BAA_QST_CODE,
case
when BAA_QST_TYPE = 'QUE' then BAA_QUE_VALUE
when BAA_QST_TYPE = 'RVA' then (select RVA_DESC from o_Ref_values where BAA_RVA_VALUE = RVA_CODE and RVA_DOMAIN = 'CONTACT_OUTCOME')
end as answer
from
BO_ASSESSMENT_ANSWERS left join o_persons on BAA_SUBJECT_ID = PER_ID
where
BAA_QSA_ID='A1457'
to_date(BAA_ASM_END_DATE,'DD/MM/YYYY') = to_Date(SYSDATE,'DD/MM/YYYY')
and
(BAA_QST_CODE = 'CONTACT_OUTCOME' or BAA_QST_DESC = 'Name of Person or Organisation of contact and their contact details')
order by 3
)
PIVOT
(
MAX(ANSWER)
for BAA_QST_CODE in ('CONTACT_OUTCOME','PER_ORG_OF_CONTACT')
)
The table that comes out of it looks quite normal except that the last two columns, the ones created from the pivot have the names surrounded by the single quotes as shown in the SQL that creates the columns

I think actually I may now be on track to solving this based on a post I saw just before I closed down last night.
The problem with being able to query the temporary table was based on the fact that I was incorrectly creating the columns in the pivot table
the Pivot should read
PIVOT
(
MAX(ANSWER)
for BAA_QST_CODE in ('CONTACT_OUTCOME' as CONTACT_OUTCOME ,'PER_ORG_OF_CONTACT' as PER_ORG_OF_CONTACT )
)
The column names then become queryable and I presume / hope I should be able to extract from the temporary table into a CSV file

Related

How to generate a hyperlink tag to each string in column, to create a tag cloud

I have a table in SQL Server that stores info from a blog, so each record is a post. One of the columns stores the tags related to each post.
I need to create a tag for each tag stored in that column, they are separated with a comma ','.
For example: For the record 1, the column "tags" stores "cars,planes,boats".
I need to generate a column upon SELECT command that will write this:
cars<a><a href="blog-tags.aspx?tag=planes">planesboats
Any help would be highly appreciated! Thanks
If there's some kind of coding layer between the data and the user, then it's probably best to do this in the coding layer.
If you really must do it in sql, you could first convert the column into a separate temporary table, then join with the temporary table (and select accordingly)
INSERT INTO #tempTable(primaryKey, data)
SELECT yt.primaryKey, s.data
FROM
YourTable yt
inner join dbo.Split(yt.tags, ',') s
Relies on a split function such as found here: split function (which isn't a very fast one... but will suffice)
Then....
Select yt.*,
'<a href="blog-tags.aspx?tag=' + t.data + '">' + t.data + '<a>' as Link
from
YourTable yt
inner join #tempTable t on yt.PrimaryKey = t.PrimaryKey

How should I temporarily store data within a PL/SQL procedure?

I am very new to PL/SQL. I have data in an initial table, named 'FLEX_PANEL_INSPECTIONS' that I am attempting to summarise in a second table, named 'PANEL_STATUS_2' using a PL/SQL procedure. However, due to the nature of the data, I have had to write a case statement in order to correctly summarise the data from FLEX_PANEL_INSPECTIONS. I have therefore created a third, intermediate table to bridge the two (named 'PANEL_STATUS_1') since the case statement will not allow columns in the group by clause which specifically order the data (to the extent of my knowledge - I get an error when I try and do this). I do not want to be storing data in the intermediate table - is there any way that I can either make it temporary (i.e. exist only while the procedure runs so that data from 'PANEL_STATUS_1' is not retained); create a view within the procedure, or remove the need for the intermediate table altogether?
Any help or criticism of my mistakes / misunderstanding of PL/SQL would be greatly appreciated. Here is the code I have written:
create or replace procedure PANEL_STATUS_PROCEDURE (panel_lot_id in number) as
begin
--Populate intermediate table with information about the status of the panels.
insert into PANEL_STATUS_1 (FLEX_LOT_ID, FLEX_PANEL_DMX, FLEX_PANEL_STATUS)
select FLEX_LOT_ID, FLEX_PANEL_DMX,
--Sum the status values of the 4 panel inspections. A panel passes if and only if this sum = 4.
case sum (FLEX_PANEL_STATUS)
when 4 then 1
else 0
end as new_panel_status
from FLEX_PANEL_INSPECTIONS
where FLEX_LOT_ID = panel_lot_id
group by FLEX_LOT_ID, FLEX_PANEL_DMX;
--Add information about the machine ID and the upload time to this table.
insert into PANEL_STATUS_2 (FLEX_LOT_ID, FLEX_PANEL_DMX, FLEX_PANEL_STATUS, MACHINE_ID, UPLOAD_TIME)
select distinct PANEL_STATUS_1.*, MACHINE_ID, UPLOAD_TIME
from PANEL_STATUS_1, FLEX_PANEL_INSPECTIONS
where (FLEX_PANEL_INSPECTIONS.FLEX_LOT_ID = PANEL_STATUS_1.FLEX_LOT_ID
and FLEX_PANEL_INSPECTIONS.FLEX_PANEL_DMX = PANEL_STATUS_1.FLEX_PANEL_DMX)
and FLEX_PANEL_INSPECTIONS.FLEX_LOT_ID = panel_lot_id;
end PANEL_STATUS_PROCEDURE;
/
You can create your temp table as
create global temporary table gtt_panel_status
( column datatype ... )
on commit [delete|preserve] rows;
(specifying either delete or preserve in the on commit clause).
However you usually don't need a temp table. You might try a with clause (CTE), or else an inline view along lines of select x, y, z from (select your subquery here).
Edit: actually looking at your query some more, I think what you a actually need is an analytic sum, i.e. a total without aggregating. For example, something like this:
create or replace procedure panel_status_procedure
( panel_lot_id in number )
as
begin
-- Add information about the machine ID and the upload time to this table.
insert into panel_status_2
( flex_lot_id
, flex_panel_dmx
, flex_panel_status
, machine_id
, upload_time )
select distinct
flex_lot_id
, flex_panel_dmx
, case sum(flex_panel_status) over (partition by flex_lot_id, flex_panel_dmx)
when 4 then 1
else 0
end
, machine_id
, upload_time
from flex_panel_inspections pi
where pi.flex_lot_id = panel_lot_id;
end panel_status_procedure;

Compare comma separated list with individual row in table

I have to compare comma separated values with a column in the table and find out which values are not in database. [kind of master data validation]. Please have a look at the sample data below:
table data in database:
id name
1 abc
2 def
3 ghi
SQL part :
Here i am getting comma separated list like ('abc','def','ghi','xyz').
now xyz is invalid value, so i want to take that value and return it as output saying "invalid value".
It is possible if i split those value, take it in temp table, loop through each value and compare one by one.
but is there any other optimal way to do this ??
I'm sure if I got the question right, however, I would personally be trying to get to something like this:
SELECT
D.id,
CASE
WHEN B.Name IS NULL THEN D.name
ELSE "invalid value"
END
FROM
data AS D
INNER JOIN badNames B ON b.Name = d.Name
--as SQL is case insensitive, equal sign should work
There is one table with bad names or invalid values if You prefer. This can a temporary table as well - depending on usage (a black-listed words should be a table, ad hoc invalid values provided by a service should be temp table, etc.).
NOTE: The select above can be nested in a view, so the data remain as they were, yet you gain the correctness information. Otherwise I would create a cursor inside a function that would go through the select like the one above and alter the original data, if that is the goal...
It sounds like you just need a NOT EXISTS / LEFT JOIN, as in:
SELECT tmp.InvalidValue
FROM dbo.HopeThisIsNotAWhileBasedSplit(#CSVlist) tmp
WHERE NOT EXISTS (
SELECT *
FROM dbo.Table tbl
WHERE tbl.Field = tmp.InvalidValue
);
Of course, depending on the size of the CSV list coming in, the number of rows in the table you are checking, and the style of splitter you are using, it might be better to dump the CSV to a temp table first (as you mentioned doing in the question).
Try following query:
SELECT SplitedValues.name,
CASE WHEN YourTable.Id IS NULL THEN 'invalid value' ELSE NULL END AS Result
FROM SplitedValues
LEFT JOIN yourTable ON SplitedValues.name = YourTable.name

Check if a list of items already exists in a SQL database

I want to create a group of users only if the same group does not exist already in the database.
I have a GroupUser table with three columns: a primary key, a GroupId, and a UserId. A group of users is described as several lines in this table sharing a same GroupId.
Given a list of UserId, I would like to find a matching GroupId, if it exists.
What is the most efficient way to do that in SQL?
Let say your UserId list is stored in a table called 'MyUserIDList', the following query will efficiently return the list of GroupId containing exactly your user list. (SQL Server Syntax)
Select GroupId
From (
Select GroupId
, count(*) as GroupMemberCount
, Sum(case when MyUserIDList.UserID is null then 0 else 1 End) as GroupMemberCountInMyList
from GroupUser
left outer join MyUserIDList on GroupUser.UserID=MyUserIDList.UserID
group by GroupId
) As MySubQuery
Where GroupMemberCount=GroupMemberCountInMyList
There are couple of ways of doing this. This answer is for sql server only (as you have not mentioned it in your tags)
Pass the list of userids in comma seperated to a stored procedure and in the SP create a dynamic query with this and use the EXEC command to execute the query. This link will guide you in this regard
Use a table-valued parameter in a SP. This is applicable to sql server 2008 and higher only.
The following link will help you get started.
http://www.codeproject.com/Articles/113458/TSQL-Passing-array-list-set-to-stored-procedure-MS
Hope this helps.
One other solution is that you convert the input list into a table. This can be done with various approaches. Unions, temporary tables and others. A neat solution combines the answer of
user1461607 for another question here on SO, using a comma-separated string.
WITH split(word, csv) AS (
-- 'initial query' (see SQLite docs linked above)
SELECT
'', -- place holder for each word we are looking for
'Auto,A,1234444,' -- items you are looking for
-- make sure the list ends with a comma !!
UNION ALL SELECT
substr(csv, 0, instr(csv, ',')), -- each word contains text up to next ','
substr(csv, instr(csv, ',') + 1) -- next recursion parses csv after this ','
FROM split -- recurse
WHERE csv != '' -- break recursion once no more csv words exist
) SELECT word, exisiting_data
FROM split s
-- now join the key you want to check for existence!
-- for demonstration purpose, I use an outer join
LEFT OUTER JOIN (select 'A' as exisiting_data) as t on t.exisiting_data = s.word
WHERE s.word != '' -- make sure we clamp the empty strings from the split function
;
Results in:
Auto,null
A,A
1234444,null

Copy results of joined query into identical table in other database

I am trying to take the results of a select query in SQL and place them in another table in a different database. The table structure is identical. The select query is as follows;
USE Warwick
Go
Select tblOperations.Link, Project.*
From tblOperations
Inner Join Warwick.dbo.Project
On tblOperations.Link= Warwick.dbo.Project.[Project ID]
Where tblOperations.Job# = Warwick.dbo.Project.[Job Number] and
tblOperations.[Status] = 'Active' or tblOperations.[Status] = 'Pending'
The join lets me select just the jobs that are considered active. I need to copy the results into the table WCI_DB.dbo.Project, which already exists. I would lke to append and not overwrite if the record exists.
Any help would be appreciated.
Thanks.
You should tag your question with the database, which seems to be SQL Server. The SQL syntax is insert:
insert into WCI_DB.dbo.Project
<your select here>;
Normally, you want to list columns after the table name:
insert into WCI_DB.dbo.Project(list of columns>
<your select here>;
However, if this is a one-time exercise and you know the columns are the same, then it is small sin to omit them once.
To create a new table, using select into, which is documented here.
select . . .
into WCI_DB.dbo.Project
. . .