I am using MS SQL 2008 R2. One of my table have more than 10 lac rows — 1 lac is 105 or 100,000, so 10 lac is 1,000,000).
I want to bind this to ASP Gridview. I tried custom paging with page size and index. But grid not binded. Timeout Error occured.
Tried directly execute stored procedure, but it takes a long time.
How can I optimize this procedure ?
My procedure
ALTER PROCEDURE SP_LOAN_APPROVAL_GET_LIST
#USERCODE NVARCHAR(50) ,
#FROMDATE DATETIME = NULL ,
#TODATE DATETIME = NULL ,
#PAGESIZE INT ,
#PAGENO INT ,
#TOTALROW BIGINT OUTPUT
AS
BEGIN
SELECT *
FROM ( SELECT DOC_NO ,
DOC_DATE_GRE ,
EMP_CODE ,
EMP_NAME_ENG as Name ,
LOAN_AMOUNT ,
DESC_ENG as Discription ,
REMARKS ,
ROW_NUMBER() OVER(
ORDER BY ( SELECT 1 )
) AS [ROWNO]
from VW_PER_LOAN
Where isnull( POST_FLAG , 'N' ) = 'N'
and ISNULl( CANCEL_FLAG , 'N' ) != 'Y'
and DOC_DATE_GRE between ISNULL(#FROMDATE , DOC_DATE_GRE )
and ISNULL(#TODATE , DOC_DATE_GRE )
and BRANCH in ( SELECT *
FROM DBO.FN_SSP_GetAllowedBranches(#USERCODE)
)
) T
WHERE T.ROWNO BETWEEN ((#PAGENO-1)*#PAGESIZE)+1 AND #PAGESIZE*(#PAGENO)
SELECT #TOTALROW=COUNT(*)
from VW_PER_LOAN
Where isnull(POST_FLAG,'N')= 'N'
and ISNULl(CANCEL_FLAG,'N')!='Y'
and DOC_DATE_GRE between ISNULL(#FROMDATE,DOC_DATE_GRE)and ISNULL(#TODATE,DOC_DATE_GRE)
and BRANCH in ( SELECT *
FROM DBO.FN_SSP_GetAllowedBranches(#USERCODE)
)
END
Thanks
The first thing to do is to look at your execution plan and discuss it with a DBA if you don't understand it.
The obvious thing that stands out is that your where clause has pretty much every column reference wrapped in some sort of function. That makes them expressions and make the SQL optimizer unable to use any covering indices that might exist.
It looks like you are calling a table-valued function as an uncorrelated subquery. That would worry me with respect to performance. I'd probably move that out of the query. Instead run it just once and populate a temporary table.
Related
I have the following table TEMP
I want to create a pivot view using SQL, Ordered by CATEGORY ASC ,by LEVEL DESC and SET ASC and fill in the value .
Expected output:
I have tried the following code but unable to get a workaround the aggregate part which is throwing an error:
SELECT *
FROM
(SELECT
SET, LEVEL, CATEGORY, VALUE
FROM
TEMP
ORDER BY
CATEGORY ASC, LEVEL DESC, SET ASC) x
PIVOT
(value(VALUE) FOR RISK_LEVEL IN ('X','Y','Z') AND CATEGORY IN ('ABC', 'DEF', 'GHI', 'JKL')) p
Furthermore I want to know if there can be any method for dynamically adding the columns and arriving at this view for any table having the same columns (so that hardcoding can be avoided).
I know we can do this in Excel and transpose it, but I want the data to be stored in the db in this format.
A stored function(or procedure) might be created in order to create a SQL for Dynamic Pivoting, and the result set is loaded into a variable of type SYS_REFCURSOR :
CREATE OR REPLACE FUNCTION Get_Categories_RS RETURN SYS_REFCURSOR IS
v_recordset SYS_REFCURSOR;
v_sql VARCHAR2(32767);
v_cols_1 VARCHAR2(32767);
v_cols_2 VARCHAR2(32767);
BEGIN
SELECT LISTAGG( ''''||"level"||''' AS "'||"level"||'"' , ',' )
WITHIN GROUP ( ORDER BY "level" DESC )
INTO v_cols_1
FROM (
SELECT DISTINCT "level"
FROM temp
);
SELECT LISTAGG( 'MAX(CASE WHEN category = '''||category||''' THEN "'||"level"||'" END) AS "'||"level"||'_'||category||'"' , ',' )
WITHIN GROUP ( ORDER BY category, "level" DESC )
INTO v_cols_2
FROM (
SELECT DISTINCT "level", category
FROM temp
);
v_sql :=
'SELECT "set", '|| v_cols_2 ||'
FROM
(
SELECT *
FROM temp
PIVOT
(
MAX(value) FOR "level" IN ( '|| v_cols_1 ||' )
)
)
GROUP BY "set"
ORDER BY "set"';
OPEN v_recordset FOR v_sql;
RETURN v_recordset;
END;
in which I used two levels of pivoting : the first is within the inner query involving PIVOT Clause, and the second is in the outer query having the conditional aggregation logic. Notice that the order of levels should be in the descending order( Z, Y, X ) within the expected result as conforming to the description.
And then invoke
VAR rc REFCURSOR
EXEC :rc := Get_Categories_RS;
PRINT rc
from SQL Developer's Command Line in order to get the result set
Btw, avoid using reserved keywords such as set and level as in your case. I needed to quote them in order to be able to use.
I have a requirement to load distinct costcenternum and a seq_key to be inserted. I wrote a procedure but this is failing one is distinct and even after removing distinct not able to run this procedure.
Please help me to correct this query to generate a seq key and also distinct cost numbers into the division table.
CREATE OR REPLACE
PROCEDURE POPULATE_DIVISION_DIM AS
BEGIN
INSERT INTO DIVISION(
"COST_CENTER_KEY"
,"COST_CENTER_NUM"
,"COST_CENTER_DESC"
,"DIVISION_CODE"
,"DIVISION_DESC"
,"COMPANY_CODE"
,"INSERT_DT"
,"UPDATE_DT"
)
(
SELECT
cc_sequence.nextval cost_center_key
, distinct (pcaf.segment4) costcenter_num
,ffvv.description costcenter_desc
,hoi.org_information9 division
,(SELECT description
FROM hr_lookups
WHERE lookup_type = 'CAT'
AND lookup_code = hoi.org_information9)
division_desc
, ppg.segment1 company
,TRUNC(SYSDATE) insert_dt
,TRUNC(SYSDATE) update_dt
FROM
hr_organization_information hoi
, hr_all_organization_units haou
, pay_cost_allocation_keyflex pcaf
, fnd_flex_values_vl ffvv
, per_all_assignments_f paaf
, pay_people_groups ppg
WHERE 1=1
AND paaf.people_group_id = ppg.people_group_id
AND haou.cost_allocation_keyflex_id =
pcaf.cost_allocation_keyflex_id(+)
AND pcaf.segment4 = ffvv.flex_value(+)
AND (ffvv.FLEX_VALUE_SET_ID is null or ffvv.FLEX_VALUE_SET_ID=
(SELECT FLEX_VALUE_SET_ID FROM FND_FLEX_VALUE_SETS WHERE
FLEX_VALUE_SET_NAME = 'ABCD'))
AND ffvv.enabled_flag(+) = 'Y'
AND haou.organization_id = hoi.organization_id
AND hoi.org_information_context = 'XX'
)
;
COMMIT;
END POPULATE_DIVISION_DIM;
I have a function which returns a RECORD.
One of the record's columns is VARRAY.
Can someone hint me how to display the RECORD, please? (my problem is related to the VARRAY column.
create or replace TYPE phone_list_typ AS VARRAY(5) OF VARCHAR2(25);
CREATE TABLE "CUSTOMERS"
("CUSTOMER_ID" NUMBER(6,0),
"CUST_FIRST_NAME" VARCHAR2(20 BYTE)
"PHONE_NUMBERS" "OE"."PHONE_LIST_TYP" ,
"CREDIT_LIMIT" NUMBER(9,2),
"CUST_EMAIL" VARCHAR2(40 BYTE));
TYPE r_cust_det IS RECORD( CUSTOMER_ID customers.CUSTOMER_ID%TYPE
, CUST_FIRST_NAME customers.CUST_FIRST_NAME%TYPE
, PHONE_NUMBERS customers.PHONE_NUMBERS%TYPE
, CREDIT_LIMIT customers.CREDIT_LIMIT%TYPE
, CUST_EMAIL customers.CUST_EMAIL%TYPE);
CREATE OR REPLACE FUNCTION show_customer_details (n_customer_id customers.customer_id%TYPE) RETURN r_cust_det
IS
v_return r_cust_det;
BEGIN
SELECT CUSTOMER_ID
, CUST_FIRST_NAME
, PHONE_NUMBERS
, CREDIT_LIMIT
, CUST_EMAIL
INTO v_return
FROM CUSTOMERS
WHERE CUSTOMER_ID = n_customer_id;
RETURN v_return;
END show_customer_details;
This may depend on how you want it to look and what the display medium is (text file, interactive web page etc), but one way might be to list the phone numbers as a comma-separated list.
select customer_id, cust_first_name, credit_limit, cust_email
, listagg(p.column_value,', ') within group (order by p.column_value) as phone_numbers
from customers c cross join table(c.phone_numbers) p
group by customer_id, cust_first_name, credit_limit, cust_email
order by customer_id;
I'm not sure what you expect out of your show_customer_details function, though.
(btw it's not a good idea to enclose identifiers in double-quotes unless you absolutely have to.)
CREATE OR REPLACE FUNCTION show_customer_details (n_customer_id customers.customer_id%TYPE) RETURN t_cust_det PIPELINED
IS
v_return t_cust_det;
BEGIN
SELECT t1.CUSTOMER_ID
, t1.CUST_FIRST_NAME
, t2.*
, t1.CREDIT_LIMIT
, t1.CUST_EMAIL
BULK COLLECT INTO v_return
FROM CUSTOMERS t1, table(t1.phone_numbers) t2
WHERE t1.CUSTOMER_ID = n_customer_id
AND column_value is not null;
FOR i IN 1 .. v_return.count
LOOP
PIPE ROW (v_return(i));
END LOOP;
END show_customer_details;
the function call is:
select * from table(SHOW_DETAILS.SHOW_CUSTOMER_DETAILS(101));
Another solution I found, without using PIPELINED is:
Define a object type
create or replace type customers_typ
is object
( CUSTOMER_ID number(6)
, CUST_FIRST_NAME varchar2(20)
, PHONE_NUMBERS varchar2(25) --phone_list_typ
, CREDIT_LIMIT number(9, 2)
, CUST_EMAIL varchar2(40)
);
Define a new type, table of previously defined object.
create or replace type t_customers_typ is table of customers_typ;
The function become
CREATE OR REPLACE FUNCTION show_customer_details (n_customer_id customers.customer_id%TYPE) RETURN t_customers_typ
IS
v_return t_customers_typ;
BEGIN
SELECT customers_typ(t1.CUSTOMER_ID
, t1.CUST_FIRST_NAME
, t2.column_value
, t1.CREDIT_LIMIT
, t1.CUST_EMAIL)
BULK COLLECT INTO v_return
FROM CUSTOMERS t1, table(t1.phone_numbers) t2
WHERE t1.CUSTOMER_ID = n_customer_id
AND t2.column_value is not null;
return v_return;
END show_customer_details;
The function is called as the same:
select * from table(SHOW_DETAILS.SHOW_CUSTOMER_DETAILS(101));
I have 2 tables in oracle . users and profile table.
i am running following query to get results
SELECT fu.user_name ,papf.SSN
FROM apps.fnd_user fu, apps.per_all_people_f papf
WHERE fu.employee_id = papf.person_id
AND (TO_CHAR(papf.EFFECTIVE_END_DATE) IS NULL
OR papf.EFFECTIVE_END_DATE > SYSDATE )
Thing is that ,
Profile table has multiple records for SSN.
So this query is returning dupes for SSN.
I just want to get one distinct record for SSN.
for eg result is like
ABC 123
ABC 123
How do i just display unquie values for SSN
Like
ABC 123
U tried distinct but did not work
Any suggestions ?
Proper use of Distinct for your case...
SELECT Distinct fu.user_name , papf.SSN
FROM apps.fnd_user fu
, apps.per_all_people_f papf
WHERE fu.employee_id = papf.person_id
AND (TO_CHAR(papf.EFFECTIVE_END_DATE) IS NULL
OR papf.EFFECTIVE_END_DATE > SYSDATE )
Query using group by can be like below to have a single record:
SELECT fu.user_name ,
papf.SSN
FROM apps.fnd_user fu,
apps.per_all_people_f papf
WHERE fu.employee_id = papf.person_id
AND ( papf.EFFECTIVE_END_DATE IS NULL
OR papf.EFFECTIVE_END_DATE > trunc(SYSDATE) --assuming effective_end_date doesnot have timestamp with it.
)
group by fu.user_name,
papf.SSN ;
I have a name/value pair in a List<T> and needing to find the best way to pass these to a stored procedure.
Id Name
1 abc
2 bbc
3 cnn
....
...
What is the best way to accomplish this?
One way to handle this in SQL Server 2005 (prior to the availability of table valued parameters) was to pass a delimited list and use a Split function. If you are using a two-column array, you would want to use two different delimiters:
Declare #Values varchar(max)
Set #Values = '1,abc|2,bbc|3,cnn'
With SplitItems As
(
Select S.Value As [Key]
, S2.Value
, Row_Number() Over ( Partition By S.Position Order By S2.Position ) As ElementNum
From dbo.Split(#Values,'|') As S
Outer Apply dbo.Split(S.Value, ',') As S2
)
Select [Key]
, Min( Case When S.ElementNum = 1 Then S.Value End ) As ListKey
, Min( Case When S.ElementNum = 2 Then S.Value End ) As ListValue
From SplitItems As S
Group By [Key]
Create Function [dbo].[Split]
(
#DelimitedList nvarchar(max)
, #Delimiter nvarchar(2) = ','
)
RETURNS TABLE
AS
RETURN
(
With CorrectedList As
(
Select Case When Left(#DelimitedList, Len(#Delimiter)) <> #Delimiter Then #Delimiter Else '' End
+ #DelimitedList
+ Case When Right(#DelimitedList, Len(#Delimiter)) <> #Delimiter Then #Delimiter Else '' End
As List
, Len(#Delimiter) As DelimiterLen
)
, Numbers As
(
Select Row_Number() Over ( Order By c1.object_id ) As Value
From sys.columns As c1
Cross Join sys.columns As c2
)
Select CharIndex(#Delimiter, CL.list, N.Value) + CL.DelimiterLen As Position
, Substring (
CL.List
, CharIndex(#Delimiter, CL.list, N.Value) + CL.DelimiterLen
, CharIndex(#Delimiter, CL.list, N.Value + 1)
- ( CharIndex(#Delimiter, CL.list, N.Value) + CL.DelimiterLen )
) As Value
From CorrectedList As CL
Cross Join Numbers As N
Where N.Value < Len(CL.List)
And Substring(CL.List, N.Value, CL.DelimiterLen) = #Delimiter
)
Another way to handle this without table-valued parameters is to pass Xml as an nvarchar(max):
Declare #Values nvarchar(max)
Set #Values = '<root><Item Key="1" Value="abc"/>
<Item Key="2" Value="bbc"/>
<Item Key="3" Value="cnn"/></root>'
Declare #docHandle int
exec sp_xml_preparedocument #docHandle output, #Values
Select *
From OpenXml(#docHandle, N'/root/Item', 1)
With( [Key] int, Value varchar(10) )
Take a look at Arrays and Lists in SQL Server 2008 to get some ideas
SQL Server 2008 also supports this multi row values syntax
create table #bla (id int, somename varchar(50))
insert #bla values(1,'test1'),(2,'Test2')
select * from #bla
i endup using foreach <insert>
This could done through three ways.
User Defined Table Type
Json Object Parsing
XML Parsing
I tried with the first option and passed a list of pairs in User Defined Table Type. This works for me. I am posting here, it might help someone else.
The first challenge for me was to pass the list of key value pair data structure and second to loop through the list and insert the record in a table.
Step 1 : Create a User Defined Table Type. I have created with a name 'TypeMetadata'. As it is custom type, I created two attributes of type nvarchar. You can create one of type integer and second of type nvarchar.
-- Type: metadata ---
IF EXISTS(SELECT * FROM SYS.TYPES WHERE NAME = 'TypeMetadata')
DROP TYPE TypeMetadata
GO
CREATE TYPE TypeMetadata AS TABLE (
mkey nvarchar (50),
mvalue nvarchar (50)
);
GO
Step 2 : Then I created a stored procedure with name 'createfiled'
-- Procedure: createtext --
CREATE PROCEDURE [dbo].[createfield]
#name nvarchar(50),
#text nvarchar(50),
#order int,
#type nvarchar(50),
#column_id int ,
#tid int,
#metadataList TypeMetadata readonly
AS
BEGIN
--loop through metadata and insert records --
DECLARE #mkey nvarchar(max);
DECLARE #mvalue nvarchar(max);
DECLARE mCursor CURSOR LOCAL FAST_FORWARD
FOR
SELECT mkey, mvalue
FROM #metadataList;
OPEN mCursor;
FETCH NEXT FROM mCursor INTO #mkey, #mvalue; -- Initial fetch attempt
WHILE ##FETCH_STATUS = 0
BEGIN
INSERT INTO template_field_metadata (name, value, template_field_id, isProperty) values (#mkey, #mvalue, 1, 0)
PRINT 'A new metadata created with id : ' + cast(SCOPE_IDENTITY() as nvarchar);
FETCH NEXT FROM mCursor INTO #mkey, #mvalue; -- Attempt to fetch next row from cursor
END;
CLOSE mCursor;
DEALLOCATE mCursor;
END
GO
Step 3: finally I executed the stored procedure like;
DECLARE #metadataToInsert TypeMetadata;
INSERT INTO #metadataToInsert VALUES ('value', 'callVariable2');
INSERT INTO #metadataToInsert VALUES ('maxlength', '30');
DECLARE #fid INT;
EXEC [dbo].[createfield] #name = 'prefagent', #text = 'Pref Agent', #order = 1 , #type= 'prefagent', #column_id = 0, #tid = 49, #metadataList =#metadataToInsert;