Download Enabling Enterprise Provisioning and Security in Oracle Portal

Survey
yes no Was this document useful for you?
   Thank you for your participation!

* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project

Document related concepts

Entity–attribute–value model wikipedia , lookup

Open Database Connectivity wikipedia , lookup

Microsoft SQL Server wikipedia , lookup

Extensible Storage Engine wikipedia , lookup

SQL wikipedia , lookup

Functional Database Model wikipedia , lookup

Clusterpoint wikipedia , lookup

Object-relational impedance mismatch wikipedia , lookup

Oracle Database wikipedia , lookup

PL/SQL wikipedia , lookup

Database model wikipedia , lookup

Relational model wikipedia , lookup

Transcript
Aces in the Hole: Learning Advanced SQL
Techniques from the OTN Forum Pros
Greg Pike
Piocon Technologies
Greg Pike
Managing Principal
Piocon Technologies
• History: 15+ year Oracle solution provider
• Home: ChicagoLand
• Focus: Business Intelligence, Data
Warehousing, Portal, web applications,
devious SQL
• Blog: www.SingleQuery.com
• Client list includes
The Oracle Discussion Forums
• An interactive community for sharing information,
questions, and comments about Oracle products and
related technologies
• Most forums are moderated by product managers, and
all of them are frequently visited by knowledgeable
users from the community
• Anyone can read messages but must be a registered
member to post a question or response
• Posts to the SQL and PL/SQL Forum are often
answered in minutes
Source: http://www.oracle.com/technology/forums/faq.html
Who Can Learn from the OTN SQL and
PL/SQL Forum?
• Beginners: Got a question…its probably already
been answered 100 times. Please search for a
solution first, but don’t be shy to ask
• Intermediate Learners: The SQL and PL/SQL Forum
has a wealth of knowledge on every imaginable topic
• Experts: You may not feel like an expert after a visit
to this Forum
• Unique Problem: This is your real-time resource for
solutions
• Contributor: Get involved and share your expertise
Who Contributes? Introducing the Experts
and Recent Forum Post Counts:
•
•
•
•
•
•
•
•
Warren Tolentino – 4,400+
Devmiral – 2,200+
Nicolas Gasparotto – 14,900+
Marias – 1,300+
The Flying Spontinalli – 700+
Rob van Wijk – 3,900+
Michaels – 3,400+
John Spencer – 3,700+
•
•
•
•
•
•
•
•
Justin Cave – 18,700+
Billy Verreynne – 5,500+
APC – 9,500+
BluShadow – 6,200+
William Robertson – 4,500+
Volder – 950+
Yingkuan – 7000+
Kamal Kishore – 7,300+
…and 100’s of people worldwide with a passion for problem solving
Q:”If the advice is free, how good can it be?”
A: “The
Program. Enough said.”
• The Oracle ACE program formally recognizes
advocates of Oracle technology with strong credentials
as evangelists and educators
• Oracle ACE recipients are chosen based on their
significant contributions and activity in the Oracle
technical community with candidates nominated by
anyone in the Oracle Technology and Applications
communities. “
Source: http://www.oracle.com/technology/community/oracle_ace/index.html
The Oracle Aces
“Oracle ACEs are known for their strong credentials as
Oracle community enthusiasts and advocates with
candidates nominated by anyone in the Oracle
Technology and Applications communities.”
–Technical proficiency
–Oracle-related blog
–Oracle discussion forum activity
–Published white paper(s) and/or article(s)
–Presentation experience
–Beta program participant
–Oracle user group member
–Oracle certification
Just a Few of the 188 Oracle Aces
•
•
•
•
•
•
•
•
•
Cary Milsap (speaking today)
Peter Koletzke (speaking today)
Dan Norris (Piocon)
Paul Dorsey
Steven Feuerstein
Ken Jacobs
Tom Kyte
Mark Rittman
Laurent Schneider
Case Study: Examining a Recent Post
• The following post is from May 28, 2007 and elicited 33 replies
from long-time Forum contributors including Oracle Aces
• http://forums.oracle.com/forums/thread.jspa?messageID=1864354
• Only a portion of the solutions are presented here and the
supporting commentary is omitted. Some queries have been
altered to fit on the page but retain their basic logic
• Thanks to the following OTN experts who contributed to this thread
(OTN handle shown):
Warren Tolentino
Devang Bhatt (devmiral)
Nicolas Gasparotto
marias
The Flying Spontinalli
Rob van Wijk
michaels
The Original Question:
“I need your help to generate a report. I'm using
Oracle 9i database. This is our data:”
Customer_ID Question_ID
10001
1
10002
1
10002
2
10002
4
10003
3
10003
4
10004
2
10004
3
10005
1
10005
2
10005
4
10006
3
10006
4
10007
2
”I need to get the count on how many customers
answered for each set of questions.
For the above data, this should be the output:”
Questions IDs
1
1,2,4
3,4
2,3
2
Customer Count
1
2
2
1
1
“The Question IDs can have as many as 10
questions.”
“Thanks in advance.”
Examining a Few of the Posted Solutions
• Method 1: Hierarchical Query
• Method 2: COLLECT
• Method 3: XML
• Method 4: MODEL
• Method 5: Pipelined Funtions
Method 1: Hierarchical Query (Warren)
• For querying datasets that contain a parent-child
relationship between rows
• Recursively walks through the tree of relationships
from the top-down or bottom-up
• Includes tools for determining location in the hierarchy
and the nature of individual nodes
Method 1: Hierarchical Query
SELECT ci2.questions,
COUNT(ci2.cnt) “Customer Count"
FROM (SELECT ci1.customer_id,
SUBSTR(MAX(SUBSTR(SYS_CONNECT_BY_PATH
(ci1.question_id,','),2)),1,40) questions,
ci1.cnt
FROM
(
SELECT customer_id,
question_id,
ROW_NUMBER() OVER (PARTITION BY
customer_id ORDER BY
customer_id, question_id) rn,
COUNT(*) OVER (PARTITION BY
customer_id,question_id) cnt
FROM customer_inquiry
) ci1
DTSRT WITH ci1.rn = 1
CONNECT BY ci1.rn = PRIOR ci1.rn + 1
AND PRIOR ci1.customer_id = ci1.customer_id
GROUP BY ci1.customer_id, ci1.cnt
) ci2
GROUP BY questions;
Step 1: The pseudo-parent-child relationship
SELECT customer_id,
question_id,
ROW_NUMBER() OVER (PARTITION BY
customer_id ORDER BY
customer_id, question_id) rn,
COUNT(*) OVER (PARTITION BY
customer_id,question_id) cnt
FROM customer_inquiry
Customer_ID
10001
10002
10002
10002
10003
10003
10004
10004
10005
10005
10005
10006
10006
10007
Question_ID
1
1
2
4
3
4
2
3
1
2
4
3
4
2
Customer_ID Question_ID
10001
1
10002
1
10002
2
10002
4
10003
3
10003
4
10004
2
10004
3
10005
1
10005
2
10005
4
10006
3
10006
4
10007
2
RN
1
1
2
3
1
2
1
2
1
2
3
1
2
1
CNT
1
1
1
1
1
1
1
1
1
1
1
1
1
1
Step 2: Employ SYS_CONNECT_BY_PATH
SELECT ci2.questions,
COUNT(ci2.cnt) “Customer Count"
FROM (SELECT ci1.customer_id,
SUBSTR(MAX(SUBSTR(SYS_CONNECT_BY_PATH
(ci1.question_id,','),2)),1,40) questions,
ci1.cnt
FROM
(
SELECT customer_id,
question_id,
ROW_NUMBER() OVER (PARTITION BY
customer_id ORDER BY
customer_id, question_id) rn,
COUNT(*) OVER (PARTITION BY
customer_id,question_id) cnt
FROM customer_inquiry
) ci1
START WITH ci1.rn = 1
CONNECT BY ci1.rn = PRIOR ci1.rn + 1
AND PRIOR ci1.customer_id = ci1.customer_id
SYS_CONNECT_BY_PATH
returns the path of a
column value from root to
GROUP BY ci1.customer_id,
ci1.cnt
node, with column
separated by char for each row returned by
) values
ci2
CONNECT
BY condition.
GROUP BY questions;
Step 2: Employ SYS_CONNECT_BY_PATH
SELECT ci2.questions,
COUNT(ci2.cnt) “Customer Count"
FROM (SELECT ci1.customer_id,
SUBSTR(MAX(SUBSTR(SYS_CONNECT_BY_PATH
(ci1.question_id,','),2)),1,40) questions,
ci1.cnt
FROM
(
Questions IDs
1
1,2,4
3,4
2,3
2
Customer Count
1
2
2
1
1
SYS_CONNECT_BY_PATH returns the path of a column value from root to
node, with column values separated by char for each row returned by
CONNECT BY condition.
Method 2: COLLECT (Greg Pike)
• COLLECT takes as its argument a column of any type
and creates a nested table of the input type out of the
rows selected
• GROUP BY can determine how the rows are
COLLECTED
• The database creates its own collection object to hold
the data
• For the required output, CAST the results into a more
usable form
• Get the data out of the collection with a function
Method 2: The Query
SELECT SUBSTR(col,1,10) questions,
COUNT(*)
FROM
(
SELECT customer_id,
tab_to_string(
CAST(
COLLECT(question_id)
AS t_number_tab
)
) col
FROM
answers
GROUP BY customer_id
)
GROUP BY col;
Step 1: COLLECT
SELECT customer_id cust_id,
COLLECT(question_id)
FROM
customer_inquiry
GROUP BY customer_id;
Customer_ID Question_ID
10001
1
10002
1
CUST_ID COLLECT(QUESTION_ID)
10002
2
------- --------------------------------10002
4
10001 SYSTP3f5GcsxkSvyjQSG5pDbjhA==(1)
10003
3
10002 SYSTP3f5GcsxkSvyjQSG5pDbjhA==(1, 2, 4)
10003
4
10003 SYSTP3f5GcsxkSvyjQSG5pDbjhA==(3, 4)
10004
2
10004 SYSTP3f5GcsxkSvyjQSG5pDbjhA==(2, 3)
10004
3
10005 SYSTP3f5GcsxkSvyjQSG5pDbjhA==(1, 2, 4)
10005
1
10006 SYSTP3f5GcsxkSvyjQSG5pDbjhA==(3, 4)
10005
2
10007 SYSTP3f5GcsxkSvyjQSG5pDbjhA==(2)
10005
4
10006
COLLECT
takes as3its argument a column of any type and creates a nested
10006
4
table
of
the
input
type out of the rows selected.
10007
2
Step 2: CAST(COLLECT)
CREATE OR REPLACE TYPE t_number_tab AS TABLE OF NUMBER;
SELECT customer_id,
CAST(COLLECT(question_id) AS t_number_tab) col
FROM
customer_inquiry
GROUP BY customer_id;
Customer_ID Question_ID
10001
1
CUST_ID COLLECT(QUESTION_ID)
10002
1
------- --------------------10002
2
10001 T_NUMBER_TAB(1)
10002
4
10002 T_NUMBER_TAB(1, 2, 4)
10003
3
10003 T_NUMBER_TAB(3, 4)
10003
4
10004 T_NUMBER_TAB(2, 3)
10004
2
10005 T_NUMBER_TAB(1, 2, 4)
10004
3
10006 T_NUMBER_TAB(3, 4)
10005
1
10007 T_NUMBER_TAB(2)
10005
2
10005
4
10006
CAST
converts one3 built-in data type or collection-typed value into another
10006
4
built-in
data
type
or
10007
2 collection-typed value.
Step 3: Simple tab_to_string Function
CREATE OR REPLACE TYPE t_number_tab AS TABLE OF NUMBER;
CREATE OR REPLACE FUNCTION tab_to_string
(
p_number_tab IN t_number_tab,
p_delimiter
IN VARCHAR2 DEFAULT ',‘
) RETURN VARCHAR2 IS
l_string VARCHAR2(32767);
BEGIN
FOR i IN p_number_tab.FIRST .. p_number_tab.LAST LOOP
IF i != p_number_tab.FIRST THEN
l_string := l_string || p_delimiter;
END IF;
l_string := l_string || to_char(p_number_tab(i));
END LOOP;
RETURN l_string;
END tab_to_string;
Step 3: tab_to_string(CAST(COLLECT))
SELECT customer_id,
tab_to_string(CAST(COLLECT(question_id) AS
t_number_tab)) col
FROM
customer_inquiry
GROUP BY customer_id;
Customer_ID Question_ID
10001
1
10002
1
10002
2
10002
4
10003
3
10003
4
10004
2
10004
3
10005
1
10005
2
10005
4
3
The 10006
tab_to_string procedure
10006
4
separated
VARCHAR2.
10007
2
CUST_ID COLLECT(QUESTION_ID)
------- --------------------10001
1
10002
1,2,4
10003
3,4
10004
2,3
10005
1,2,4
10006
3,4
10007
2
converts a table of numbers to a comma-
Method 2: Putting it all together
SELECT SUBSTR(col,1,10) questions,
COUNT(*)
FROM
(
SELECT customer_id,
tab_to_string(
Questions IDs
Customer Count
CAST(
1
1
COLLECT(question_id)
1,2,4
2
AS t_number_tab
3,4
2
) 2,3
1
) col
2
1
FROM
answers
GROUP BY customer_id
)
GROUP BY col;
Method 3: XML (The Flying Spontinalli)
• XML functions:
– Convert traditional table data into XML
– Inquire upon XML data and fragments
– Operate on XML data and fragments
– Convert XML data back to character data type
• CURSOR function: Converts a sub-query into a REF CURSOR
Method 3: The Query
SELECT REPLACE(REPLACE(REPLACE(
questions,'</Q>'||CHR(10)||'<Q>',','
),'<Q>',NULL
),'</Q>',NULL
) questions,
COUNT(*) the_count
FROM (
SELECT DISTINCT customer_id,
EXTRACT (
SYS_XMLGEN(seq),'/SEQ/XMLTYPE/ROW/Q'
).getstringval() questions
FROM
(
SELECT customer_id,
XMLSEQUENCE (
CURSOR(
SELECT question_id Q
FROM TEST t2
WHERE customer_id = t.customer_id
)
) seq
FROM TEST t
)
)
GROUP BY questions;
Step 1: CURSOR and XMLSEQUENCE
SELECT customer_id,
XMLSEQUENCE(
CUST SEQ
----- -----------------------------10001 XMLSEQUENCETYPE(XMLTYPE( <ROW>
<Q>1</Q>
CURSOR(
</ROW>
SELECT question_id))Q
FROM
TEST t2
10002 XMLSEQUENCETYPE(XMLTYPE(
WHERE customer_id
= t.customer_id <ROW>
<Q>1</Q>
</ROW>
), XMLTYPE( <ROW>
) seq
<Q>2</Q>
FROM TEST t;
</ROW>
), XMLTYPE(
<ROW>
The CURSOR function converts a sub-query into a REF CURSOR.
In this
case,
<Q>4</Q>
XMLSEQUENCE requires a REF CURSOR.
</ROW>
)
The XMLSEQUENCE operator is used to split multi-value results from XMLTYPE
queries into multiple rows.
Step 2: SYS_XMLGen and EXTRACT
SELECT DISTINCT customer_id,
EXTRACT(
SYS_XMLGEN(seq),'/SEQ/XMLTYPE/ROW/Q'
).getstringval () questions
FROM
(
SELECT customer_id,
XMLSEQUENCE(
CURSOR(
SELECT question_id Q
FROM TEST t2
WHERE customer_id = t.customer_id
)
) seq
FROM TEST t
)
The SYS_XMLGen function takes an expression that evaluates to a particular row
and column of the database, and returns an instance of type XMLType containing
an XML document
Applying EXTRACT to an XMLType value extracts the node or a set of nodes from
the document identified by the XPath expression. The method getStringVal()
retrieves the text from the XMLType instance
Step 2: SYS_XMLGen and EXTRACT
SELECT DISTINCT customer_id,
EXTRACT(
SYS_XMLGEN(seq),'/SEQ/XMLTYPE/ROW/Q'
).getstringval () questions
CUST SEQ
----- -----------------------------10002 XMLSEQUENCETYPE(XMLTYPE( <ROW>
<Q>1</Q>
</ROW>
), XMLTYPE( <ROW>
<Q>2</Q>
</ROW>
), XMLTYPE( <ROW>
<Q>4</Q>
</ROW>
CUST
----10001
10002
10003
10004
10005
10006
10007
QUESTIONS
------------------------<Q>1</Q>
<Q>1</Q><Q>2</Q><Q>4</Q>
<Q>3</Q><Q>4</Q>
<Q>2</Q><Q>3</Q>
<Q>1</Q><Q>2</Q><Q>4</Q>
<Q>3</Q><Q>4</Q>
<Q>2</Q>
The SYS_XMLGen function takes an expression that evaluates to a particular row
and column of the database, and returns an instance of type XMLType containing
an XML document
Applying EXTRACT to an XMLType value extracts the node or a set of nodes from
the document identified by the XPath expression. The method getStringVal()
retrieves the text from the XMLType instance
Method 3: Back to the Query
SELECT REPLACE(REPLACE(REPLACE(
questions,'</Q>'||CHR(10)||'<Q>',','
),'<Q>',NULL
),'</Q>',NULL
Questions IDs
Cust Cnt
) questions,
COUNT(*) the_count
1
1
FROM (
1,2,4
2
SELECT DISTINCT customer_id,
3,4
2
EXTRACT (
SYS_XMLGEN(seq),'/SEQ/XMLTYPE/ROW/Q'
2,3
1
).getstringval() questions
2
1
FROM
(
SELECT customer_id,
XMLSEQUENCE (
CURSOR(
SELECT question_id Q
FROM TEST t2
WHERE customer_id = t.customer_id
)
) seq
FROM TEST t
)
)
GROUP BY questions;
Method 4: MODEL (Rob van Wijk)
SELECT q "Questions",
COUNT(*) "Customer Count"
FROM
(
SELECT SUBSTR(q,2) q,
rn
FROM
a
MODEL PARTITION BY (cust_id)
DIMENSION BY (ROW_NUMBER() OVER
(PARTITION BY cust_id ORDER BY quest_id DESC) rn)
MEASURES (CAST(quest_id AS varchar2(20)) q)
RULES (
q[any] ORDER BY rn DESC = q[cv()+1] || ',' || q[cv()]
)
)
WHERE rn = 1
GROUP BY q;
Method 4: The MODEL Portion of the Query
MODEL PARTITION BY (cust_id)
DIMENSION BY (ROW_NUMBER() OVER
(PARTITION BY cust_id ORDER BY quest_id DESC) rn)
MEASURES (CAST(quest_id AS varchar2(20)) q)
RULES (
q[any] ORDER BY rn DESC = q[cv()+1] || ',' || q[cv()]
)
The MODEL clause enables you to create a multidimensional array by mapping the columns of a query
into three groups: partitioning, dimension, and measure columns
* All text on this page from Oracle® Database Data Warehousing Guide
Method 4: Dissecting the MODEL Clause
MODEL PARTITION BY (cust_id)
DIMENSION BY (ROW_NUMBER() OVER
(PARTITION BY cust_id ORDER BY quest_id DESC) rn)
MEASURES (CAST(quest_id AS varchar2(20)) q)
RULES (
q[any] ORDER BY rn DESC = q[cv()+1] || ',' || q[cv()]
)
The MODEL clause enables you to create a multidimensional array by mapping the columns of a query
into three groups: partitioning, dimension, and measure columns
PARTITION columns define the logical blocks of the result set in a way similar to the partitions of the
analytical functions. Rules in the MODEL clause are applied to each partition independent of other
partitions
* All text on this page from Oracle® Database Data Warehousing Guide
Method 4: Dissecting the MODEL Clause
MODEL PARTITION BY (cust_id)
DIMENSION BY (ROW_NUMBER() OVER
(PARTITION BY cust_id ORDER BY quest_id DESC) rn)
MEASURES (CAST(quest_id AS varchar2(20)) q)
RULES (
q[any] ORDER BY rn DESC = q[cv()+1] || ',' || q[cv()]
)
The MODEL clause enables you to create a multidimensional array by mapping the columns of a query
into three groups: partitioning, dimension, and measure columns
PARTITION columns define the logical blocks of the result set in a way similar to the partitions of the
analytical functions. Rules in the MODEL clause are applied to each partition independent of other
partitions
DIMENSION columns define the multi-dimensional array and are used to identify cells within a partition.
By default, a full combination of dimensions should identify just one cell in a partition
* All text on this page from Oracle® Database Data Warehousing Guide
Method 4: Dissecting the MODEL Clause
MODEL PARTITION BY (cust_id)
DIMENSION BY (ROW_NUMBER() OVER
(PARTITION BY cust_id ORDER BY quest_id DESC) rn)
MEASURES (CAST(quest_id AS varchar2(20)) q)
RULES (
q[any] ORDER BY rn DESC = q[cv()+1] || ',' || q[cv()]
)
The MODEL clause enables you to create a multidimensional array by mapping the columns of a query
into three groups: partitioning, dimension, and measure columns
PARTITION columns define the logical blocks of the result set in a way similar to the partitions of the
analytical functions. Rules in the MODEL clause are applied to each partition independent of other
partitions
DIMENSION columns define the multi-dimensional array and are used to identify cells within a partition.
By default, a full combination of dimensions should identify just one cell in a partition
MEASURES are equivalent to the measures of a fact table in a star schema.
* All text on this page from Oracle® Database Data Warehousing Guide
Method 4: Dissecting the MODEL Clause
MODEL PARTITION BY (cust_id)
DIMENSION BY (ROW_NUMBER() OVER
(PARTITION BY cust_id ORDER BY quest_id DESC) rn)
MEASURES (CAST(quest_id AS varchar2(20)) q)
RULES (
q[any] ORDER BY rn DESC = q[cv()+1] || ',' || q[cv()]
)
The MODEL clause enables you to create a multidimensional array by mapping the columns of a query
into three groups: partitioning, dimension, and measure columns
PARTITION columns define the logical blocks of the result set in a way similar to the partitions of the
analytical functions. Rules in the MODEL clause are applied to each partition independent of other
partitions
DIMENSION columns define the multi-dimensional array and are used to identify cells within a partition.
By default, a full combination of dimensions should identify just one cell in a partition
MEASURES are equivalent to the measures of a fact table in a star schema
RULES are used to manipulate the measure values of the cells in the multi-dimensional array defined
by partition and dimension columns
* All text on this page from Oracle® Database Data Warehousing Guide
Method 4: Dissecting the MODEL Clause
quest_id
Cust ID
CV()
---------PARTITION BY (cust_id)
ROW_NUMBER
(rn)
Q
------------
10001
1
1
,1
10002
1
3
,1
10002
2
2
,1,2
10002
4
1
,1,2,4
10003
3
2
,3
10003
4
1
,3,4
10004
2
2
,2
10004
3
1
,2,3
10005
1
3
1
10005
2
2
,1,2
10005
4
1
,1,2,4
RULES (
10006
q[any] ORDER BY rn DESC =
10006
q[cv(rn)+1] || ',' ||q[cv(rn)]
10007
)
3
2
,3
4
1
,3,4
2
1
,2
DIMENSION BY (ROW_NUMBER() OVER
(PARTITION BY cust_id
ORDER BY quest_id DESC) rn)
MEASURES (CAST(quest_id
AS varchar2(20)) q)
Example:
q[2] = q[3]||’,’||q[2] = NULL,3
q[1] = q[2]||’,’||q[1] = ,3,4
Method 4: Back to the Query
SELECT q "Questions",
COUNT(*) "Customer Count"
FROM
(
SELECT SUBSTR(q,2) q,
rn
FROM
a
Questions IDs
1
1,2,4
3,4
2,3
2
Customer Count
1
2
2
1
1
MODEL PARTITION BY (cust_id)
DIMENSION BY (ROW_NUMBER() OVER
(PARTITION BY cust_id ORDER BY quest_id DESC) rn)
MEASURES (CAST(quest_id AS varchar2(20)) q)
RULES (
q[any] ORDER BY rn DESC = q[cv()+1] || ',' || q[cv()]
)
)
WHERE rn = 1
GROUP BY q;
Method 5: The Pipelined Function (michaels)
• Oracle Table Functions produce a collection of rows that can be
consumed by a query much like a table.
• Rows from a collection returned by a table function can also be
PIPELINED or returned as they are produced instead of in a
complete set upon function completion.
• Two supported approached for Pipelining:
– Interface method
– PL/SQL method
Method 5: The Query
SELECT
COLUMN_VALUE "Questions",
COUNT (*)
"Customer count"
FROM
TABLE (f ())
GROUP BY COLUMN_VALUE;
Oracle Table Functions produce a collection of rows that can be
consumed by a query much like a table.
Method 5: The Pipelined Function
CREATE OR REPLACE FUNCTION f
RETURN SYS.dbms_debug_vc2coll PIPELINED
AS
l_c1
VARCHAR2 (20);
l_c2
PLS_INTEGER;
BEGIN
FOR c IN
(SELECT a.*,
COUNT (*) OVER (PARTITION BY customer_id) cnt,
ROW_NUMBER () OVER (PARTITION BY customer_id
ORDER BY question_id) rn
FROM a
ORDER BY customer_id, question_id)
LOOP…
Method 5: The Pipelined Function continued
LOOP
IF l_c2 = c.customer_id OR l_c2 IS NULL THEN
l_c1 := l_c1 || c.question_id || ',';
l_c2 := c.customer_id;
IF c.cnt = c.rn THEN
PIPE ROW (RTRIM (l_c1, ','));
l_c1 := NULL;
l_c2 := NULL;
END IF;
END IF;
END LOOP;
RETURN;
END f;
Method 5: The Pipelined Function
SELECT
COLUMN_VALUE "Questions",
COUNT (*)
"Customer count"
FROM
TABLE (f ())
GROUP BY COLUMN_VALUE;
Questions IDs
1
1,2,4
3,4
2,3
2
Customer Count
1
2
2
1
1
So many choices, so little time…
•
If all these queries provide the same results, which one should
be chosen?
• Query cost information was added to this post - yet another
topic!
• The results:
1. Michaels PIPELINED Function
2. Rob's MODEL
3. Nicolas' HIERARCHY 1
4. Nicolas' HIERARCHY 2
5. Greg's COLLECT
6. Warren/devmiral's HIERARCHY
7. Michaels'/Greg‘s XML
Not done yet! Even More Solutions…
•
•
•
•
XML combined with COLLECT
Bit Pattern
User-defined Aggregate Functions (ODCIAggregate Interface)
Old-school Oracle 7 solution
Wow, All of These Topics in a Single Thread!
•
•
•
•
•
•
•
•
Hierarchical Queries
SYS_CONNECT_BY_ROOT
Analytic Functions
COLLECT
CAST
CURSOR
•
•
•
•
•
•
XML Functions
MODEL Clause
Pipelined Functions
Bit Pattern Techniques
User-defined Aggregates
Explain Plans/Query Costs
BUT…Never underestimate the value of using widelyunderstood and accepted Oracle database concepts
The experts here used this thread to demonstrate alternate
technologies only and never advocated these rather esoteric
solutions
Jumping in the Pool: “Forum-Decorum”
• Don’t demand urgent help. In fact, don’t demand anything from
this all-volunteer community
• Search for an answer first
• Use the proper code notation tags – reformatting queries is a pain
[pre]This is code text[/pre]
• Don’t flame the Gurus…you will likely get scorched!
• If you use information from the Forums on your Blog or anywhere
else, please reference your source
• Don’t be intimidated. If you don’t understand an answer, request
clarification
• Thank your responders
Wrap-Up
• The Oracle SQL and PL/SQL Forum has 64K+ topics with almost
350k posts
• Other active Forums include:
– Database-General
– App Server
– J-Developer
– Forms
– OWB
– More!
• Take advantage of the experts including many Oracle Aces!
• Learn something new
• Become a regular contributor!
Acknowledgements
•
•
•
•
•
•
Oracle® Database Data Warehousing Guide 10gR2
Oracle® Database SQL Reference 10gR2
Oracle® XML DB Developer's Guide 11gR1
OTN - Getting into SQL/XML - Tim Quinlan
http://www.oracle-base.com
The OTN SQL and PL/SQL Forum
Thank You
for attending!
Thank
You
for attending!
Greg Pike
[email protected]
Piocon Technologies
1420 Kensington Rd. Suite 106
Oak Brook, IL 60523
630-579-0800
Blog: www.singlequery.com
Thanks to numerous contributors:
– The Oracle Forum experts