How to use WITH clause with UNION ALL in oracle SQL - sql

GOOD Day for you guys , i have a question regarding the with clause with union all the select statment in the procudre is working fine but when i'm trying to add condiotion to each with clause it giving this error ("Inappropriate into") the below is an example of what i'm trying to do :
-------------------------------------------SOO1
WITH SS AS (( SELECT /) */
COUNT(ROWNUM) AS S001
FROM x.MSSE A
WHERE (SUCCESS_STATUS='F'
UNION ALL
SELECT
COUNT(ROWNUM) AS S001
FROM X.MSSE A
WHERE (SUCCESS_STATUS='N'
),
S333 AS
(SELECT /*+ INDEX (A MSSEADTD_SI02) */
COUNT(ROWNUM) AS S333_
FROM X.MSSE A
WHERE (SUCCESS_STATUS='M' )
union all
SELECT /*+ INDEX (A MSSEORMD_SI03) */
COUNT(ROWNUM) AS S333_
FROM HHL7.MSSEORMD A
WHERE (SUCCESS_STATUS='A'
),
LIS AS
(SELECT /*+ INDEX (A MSSEORUD_SI02) */
COUNT(ROWNUM)AS LIS_
FROM A.MSS A
WHERE (SUCCESS_STATUS='D')
SELECT SUM(S001) INTO Value_A
FROM
(
SELECT S001 FROM SS
)
UNION ALL
SELECT SUM(S333_) INTO VALUE_C
FROM
(SELECT S333_ FROM S333)
UNION ALL
SELECT SUM(LIS_)INTO VALUE_D FROM
(SELECT LIS_ FROM LIS)
IF Value_A > 30000 THEN
--DO THIS
IF VALUE_C > 30000 THEN
--DO THAT
--...ETC

The way I see it, it is UNION - but somewhat different from the one you wrote because part of data comes from one table (x.msse) and another from hh17.msseormd. It is unclear whether you can (or can not) join these two tables so ... union can be one option.
As number and datatype of columns returned by select statements in union must match, the 2nd select selects two 0 constant values.
The final select sums values returned in the CTE.
with temp as
(-- values from X.MSSE table
select
sum(case when success_status in ('F', 'N') then 1 else 0 end) s001,
sum(case when success_status in ('M') then 1 else 0 end) s333,
sum(case when success_status in ('D') then 1 else 0 end) lis
from x.msse
union all
-- values from HH17.MSSEORMD table
select
0 s001,
sum(case when success_status in ('A') then 1 else 0 end) s333,
0 lis
from hhl7.msseormd
)
select sum(s001), sum(s333), sum(lis)
into value_a, value_c, value_d
from temp;
IFs follow, but - that's easier part, once you fetch value_a etc.
[EDIT, based on your comment]
If that's the case, then one "huge" CTE won't help at all. I'd suggest you to split it into several parts, one CTE per target variable. Something like this:
-- values that make VALUE_A: ------------------------
with ss as
(select count(*) cnt from msse where success_status in ('F', 'N')
union all
select count(*) cnt from abcd where xyz = 23
union all
...
)
select sum(cnt)
into value_a
from ss;
-- values that make VALUE_C: ------------------------
with s333 as
(select count(*) cnt from msse where success_status in ('M')
union all
select count(*) cnt from msseormd where success_status = 'A'
union all
select count(*) cnt from qwerty where zzy = 154
union all
...
)
select sum(cnt)
into value_c
from s333;
-- etc., for all VALUE_x variables
As of error you got: you can't have multiple INTO clauses in the same SELECT statement. You could, though, do this:
with (your current CTE goes here)
-- final SELECT:
select (select sum(s001_) from ss),
(select sum(s333_) from s333),
(select sum(lis_) from lis)
into value_a, value_c, value_d
from dual
but I'd say that - if there are dozens of tables involved, one huge CTE really doesn't help at all, but makes things more complex than they should be.

Your query is equivalent to just:
SELECT set_a.S001, set_c.S333_, set_d.LIS_
INTO Value_A, VALUE_C, VALUE_D
FROM (SELECT COUNT(1) AS S001
FROM x.MSSE
WHERE SUCCESS_STATUS IN ('N','F')) set_a
, (SELECT COUNT(1) AS S333_
FROM X.MSSE
WHERE SUCCESS_STATUS IN ('M','A')) set_c
, (SELECT COUNT(1) AS LIS_
FROM A.MSS A
WHERE SUCCESS_STATUS = 'D') set_d;
IF Value_A > 30000 THEN
--DO THIS
END IF;
IF VALUE_C > 30000 THEN
--DO THAT
END IF;
There is 0 need of those unions.
EDIT: If you have different tables you can adopt either of these methods:
/* Preserve unions and use it as subquery*/
SELECT SUM(S333_)
INTO VALUE_C
FROM (SELECT /*+ INDEX (A MSSEADTD_SI02) */
COUNT(ROWNUM) AS S333_
FROM X.MSSE A
WHERE SUCCESS_STATUS = 'M'
UNION ALL
SELECT /*+ INDEX (A MSSEORMD_SI03) */
COUNT(ROWNUM)
FROM HHL7.MSSEORMD A
WHERE SUCCESS_STATUS = 'A')
/* Use scalar subquery */
SELECT (SELECT /*+ INDEX (A MSSEADTD_SI02) */
COUNT(ROWNUM) AS S333_
FROM X.MSSE A
WHERE SUCCESS_STATUS = 'M')
+ (SELECT /*+ INDEX (A MSSEORMD_SI03) */
COUNT(ROWNUM)
FROM HHL7.MSSEORMD A
WHERE SUCCESS_STATUS = 'A')
INTO VALUE_C
FROM dual

Related

Excluding records from table based on rules from another table

I'm using Oracle SQL and I have a product table with diffrent attributes and sales volume for each product and another table with certain exclusion rules for different level of aggregation. Let's look at the example:
Here is our main table with sales data on which we want to perform some calculations:
And the other table contains diffrent rules which are supposed to exclude certain rows from table above:
When there is an "x", this column shouldn't be considered so our rules are:
1. exclude all rows with ATTR_3 = 'no'
2. exlcude all rows with ATTR_1 = 'Europe' and ATTR_2 = 'snacks' and ATTR_3 = 'no'
3. exlcude all rows with ATTR_1 = 'Africa'
And based on that our final output should be like that:
How this could be achived in SQL? I was thinking about join but I have no idea how to handle different levels of aggregation for exclusions.
I think your expected output is wrong. None of the rules excludes the 2nd row (Europe - snacks - yes).
SQL> with
2 -- sample data
3 test (product_id, attr_1, attr_2, attr_3) as
4 (select 81928 , 'Europe', 'beverages', 'yes' from dual union all
5 select 16534 , 'Europe', 'snacks' , 'yes' from dual union all
6 select 56468 , 'USA' , 'snacks' , 'no' from dual union all
7 select 129921, 'Africa', 'drinks' , 'yes' from dual union all
8 select 123021, 'Africa', 'snacks' , 'yes' from dual union all
9 select 165132, 'USA' , 'drinks' , 'yes' from dual
10 ),
11 rules (attr_1, attr_2, attr_3) as
12 (select 'x' , 'x' , 'no' from dual union all
13 select 'Europe', 'snacks', 'no' from dual union all
14 select 'Africa', 'x' , 'x' from dual
15 )
16 -- query you need
17 select t.*
18 from test t
19 where (t.attr_1, t.attr_2, t.attr_3) not in
20 (select
21 decode(r.attr_1, 'x', t.attr_1, r.attr_1),
22 decode(r.attr_2, 'x', t.attr_2, r.attr_2),
23 decode(r.attr_3, 'x', t.attr_3, r.attr_3)
24 from rules r
25 );
PRODUCT_ID ATTR_1 ATTR_2 ATT
---------- ------ --------- ---
81928 Europe beverages yes
16534 Europe snacks yes
165132 USA drinks yes
SQL>
You can use the join using CASE .. WHEN statement as follows:
SELECT P.*
FROM PRODUCT P
JOIN RULESS R ON
(R.ATTR_1 ='X' OR P.ATTR_1 <> R.ATTR_1)
AND (R.ATTR_2 ='X' OR P.ATTR_2 <> R.ATTR_2)
AND (R.ATTR_3 ='X' OR P.ATTR_3 <> R.ATTR_3)
You can use NOT EXISTS
SELECT *
FROM sales s
WHERE NOT EXISTS (
SELECT 0
FROM attributes a
WHERE ( ( a.attr_1 = s.attr_1 AND a.attr_1 IS NOT NULL )
OR a.attr_1 IS NULL )
AND ( ( a.attr_2 = s.attr_2 AND a.attr_2 IS NOT NULL )
OR a.attr_2 IS NULL )
AND ( ( a.attr_3 = s.attr_3 AND a.attr_3 IS NOT NULL )
OR a.attr_3 IS NULL )
)
where I considered the x values within the attributes table as NULL. If you really have x characters, then you can use :
SELECT *
FROM sales s
WHERE NOT EXISTS (
SELECT 0
FROM attributes a
WHERE ( ( NVL(a.attr_1,'x') = s.attr_1 AND NVL(a.attr_1,'x')!='x' )
OR NVL(a.attr_1,'x')='x' )
AND ( ( NVL(a.attr_2,'x') = s.attr_2 AND NVL(a.attr_2,'x')!='x' )
OR NVL(a.attr_2,'x')='x' )
AND ( ( NVL(a.attr_3,'x') = s.attr_3 AND NVL(a.attr_3,'x')!='x' )
OR NVL(a.attr_3,'x')='x' )
)
instead.
Demo
I would do this with three different not exists:
select p.*
from product p
where not exists (select 1
from rules r
where r.attr_1 = p.attr_1 and r.attr_1 <> 'x'
) and
not exists (select 1
from rules r
where r.attr_2 = p.attr_2 and r.attr_2 <> 'x'
) and
not exists (select 1
from rules r
where r.attr_3 = p.attr_3 and r.attr_3 <> 'x'
) ;
In particular, this can take advantage of indexes on (attr_1), (attri_2) and (attr_3) -- something that is quite handy if you have a moderate number of rules.

SQL case when with equality and count conditions

How can i perform a query with filtering both value and count of the element.Such as
Select
(case
when element = 'data1' and (select count(element) from mytable where element='data1') > 15 then '1'
when element = 'data2' and (select count(element) from mytable where element='data2') > 15 then '2'
.
.
.
)
from mytable
where conditions
are there any quick and simple ways to implement this?
I think you want window functions:
select (case when element = 'data1' and
count(*) over (partition by element) > 15
then '1'
when element = 'data2' and
count(*) over (partition by element) > 15
then '2'
.
.
.
)
from mytable
where conditions
For both code clarity and performance reason, I would separate the aggregating in a CTE and then invoke it in a join. If the table is big perhaps make sense you put the result in a temporary table instead of CTE for performance reasons.
;WITH ElementCTE
AS
(
SELECT element, count(Element) AS count_Element
FROM mytable
GROUP BY element
WHERE Conditions
)
SELECT
CASE ELEMENT
WHEN 'Data1' AND count_Element > 15 THEN '1'
WHEN 'Data2' AND count_Element > 15 THEN '2'
FROM mytable AS mt
INNER JOIN Element AS el
ON mt.Element = el.Element
WHERE mt.conditions
Assuming you have a table:
CREATE TABLE NULLTEST
(
TransactioNo INT,
Code VARCHAR(25)
)
INSERT INTO NULLTEST VALUES (NULL, 'TEST1');
INSERT INTO NULLTEST VALUES (NULL, 'TEST2');
INSERT INTO NULLTEST VALUES (1, 'TEST2');
The query could look like this:
SELECT
CASE
WHEN Code = 'TEST2' AND
(SELECT COUNT(1) FROM dbo.NULLTEST n WHERE n.Code = 'TEST2')> 1 THEN '1'
WHEN Code = 'TEST1' AND
(SELECT COUNT(1) FROM dbo.NULLTEST n WHERE n.Code ='TEST1')> 1 THEN '2'
ELSE '3' end Yourcolumn
FROM dbo.NULLTEST t
WHERE ...

how to use unique value to stop data from replicating?

I have this excel document and I import the sheet from that file into my database after that I use this code to put them in separate tables:
insert into LP_Pending_Info(Service_order,Company,Country , HQ_AgingBase_Date ,PENDING_DAYS, Posting_Date,Service_Type,Service_TypeText,SUB_SVC_Type,Status,Status_Text,Reason,Reason_Text,Reason_Aging,SVC_Comment,ASC_Code,ASC_Name,ASC_JobNo,Model,CIC_Product,SerialNo,IN_OUT_WTY,IMEI,Defect_DESC,Detail_Type,Detail_TypeText,Repair_RCV_DT,Repair_RCV_TM,Complete_DT,SVC_Level,Consumer,Consumer_Text,TelNumber,City1,Street,Engineer_Code)
select Service_order,Company,Country , [HQ aging base date] ,PENDING_DAYS, Posting_Date,Service_Type,SERVICE_TYPE_TXT,SUB_SVC_Type,Status,Status_Text,Reason,Reason_Text,Reason_Aging,SVC_Comment,[ASC code],[ASC
name],ASC_JOB_NO,Model,CIC_PRD,SERIAL_NO,INOUTWTY,IMEI,Defect_DESC,Detail_Type,DETAIL_TYPE_TEXT,Repair_RCV_DT,Repair_RCV_TM,Complete_DT,SVC_Level,Consumer,consumer_txt,TEL_NUMBER,City1,Street,[Engineer code]
from LP_Pending_Jobs;
insert into LP_Part_Codes(Service_order, PartCode,serPluspart)
select Service_order, Part_code , CONVERT(nvarchar(150), Service_order)+Part_code
from LP_Pending_Jobs
cross apply (
--unpivot
select Part_code1 as Part_code where len(Part_code1) > 0
union all
select Part_code2 where len(Part_code2) > 0
union all
select Part_code3 where len(Part_code3) > 0
union all
select Part_code4 where len(Part_code4) > 0
union all
select Part_code5 where len(Part_code5) > 0
) unp;
insert into LP_PS_Codes(Service_Order, PS)
select Service_order,PS
from LP_Pending_Jobs
cross apply (
select PS1 as PS where len(PS1)>0
union all
select PS2 where len(PS2) > 0
union all
select PS3 where len(PS3) > 0
union all
select PS4 where len(PS4) > 0
union all
select PS5 where len(PS5) > 0
) unp;
insert into LP_Confirmation_Codes(Service_Order, SO_NO)
select Service_order,SO
from LP_Pending_Jobs
cross apply (
select confirmation_No1 as SO where len(confirmation_No1)>0
union all
select SO_NO2 where len(SO_NO2) > 0
union all
select SO_NO3 where len(SO_NO3) > 0
union all
select SO_NO4 where len(SO_NO4) > 0
union all
select SO_NO5 where len(SO_NO5) > 0
) unp;
----------------
insert into LP_QTY(Service_Order, QTY)
select Service_order,QTY
from LP_Pending_Jobs
cross apply (
select QTY1 as QTY where len(QTY1)>0
union all
select QTY2 where len(QTY2) > 0
union all
select QTY3 where len(QTY3) > 0
union all
select QTY4 where len(QTY4) > 0
union all
select QTY5 where len(QTY5) > 0
) unp;
insert into LP_ASC_PO_Codes(Service_Order, ASC_PO_NO)
select Service_order,ASC_PO
from LP_Pending_Jobs
cross apply (
select ASC_PO_No1 as ASC_PO where len(ASC_PO_No1)>0
union all
select ASC_PO_No2 where len(ASC_PO_No2) > 0
union all
select ASC_PO_No3 where len(ASC_PO_No3) > 0
union all
select ASC_PO_No4 where len(ASC_PO_No4) > 0
union all
select ASC_PO_No5 where len(ASC_PO_No5) > 0
) unp;
insert into LP_PO_Date(Service_Order, PO_Date)
select Service_order,PO_Date
from LP_Pending_Jobs
cross apply (
select PO_DATE1 as PO_Date where len(PO_DATE1)>0
union all
select PO_DATE2 where len(PO_DATE2) > 0
union all
select PO_DATE3 where len(PO_DATE3) > 0
union all
select PO_DATE4 where len(PO_DATE4) > 0
union all
select PO_DATE5 where len(PO_DATE5) > 0
) unp;
insert into LP_SO_Date(Service_Order, SO_Date)
select Service_order,SO_Date
from LP_Pending_Jobs
cross apply (
select SO_DATE1 as SO_Date where len(SO_DATE1)>0
union all
select SO_DATE2 where len(SO_DATE2) > 0
union all
select SO_DATE3 where len(SO_DATE3) > 0
union all
select SO_DATE4 where len(SO_DATE4) > 0
union all
select SO_DATE5 where len(SO_DATE5) > 0
) unp;
and because the data's are in one row I used cross apply to put each data in separate column.
The problem start when I join them , cause they show way more data that it should be.
here is the select code:
select distinct LP_Pending_Info.Service_Order,LP_Pending_Info.Pending_Days,
LP_Pending_Info.Service_Type,LP_Pending_Info.ASC_Code,LP_Pending_Info.Model,
LP_Pending_Info.IN_OUT_WTY, LP_Part_Codes.PartCode,LP_PS_Codes.PS,
LP_Confirmation_Codes.SO_NO,LP_Pending_Info.Engineer_Code,serPluspart
from LP_Pending_Info
inner join LP_Part_Codes on LP_Pending_Info.Service_order = LP_Part_Codes.Service_order
inner join LP_PS_Codes on LP_Part_Codes.Service_Order = LP_PS_Codes.Service_Order
inner join LP_Confirmation_Codes on LP_PS_Codes.Service_Order = LP_Confirmation_Codes.Service_Order
order by LP_Pending_Info.Service_order;
I asked around and I come to this point that I need a unique column, so I added
'serPluspart'
this column to my table in hope that it'll fix my problem but when I use my select I still see the extra data.
here is the first 25 record:
for every service order at top I have five part code, by looking at the picture the part code '4182134076' should have 2 rows because it has two part code but it shows that service order for 8 times and I don't know how to fix it. appreciate any suggestion.
the excel input file for this '4182134076' service order:
Service_order PENDING_DAYS SERVICE_TYPE ASC code MODEL INOUTWTY Part_code1 PS1 ASC_PO_No1 confirmation_No1 QTY1 PO_DATE1 SO_DATE1 Part_code2 PS2 ASC_PO_No2 SO_NO2 QTY2 PO_DATE2 SO_DATE2 Part_code3 PS3 ASC_PO_No3 SO_NO3 QTY3 PO_DATE3 SO_DATE3 Part_code4 PS4 ASC_PO_No4 SO_NO4 QTY4 PO_DATE4 SO_DATE4 Part_code5 PS5 ASC_PO_No5 SO_NO5 QTY5 PO_DATE5 SO_DATE5 Engineer code
4182134076 36 CI 4285818 SM-A310FZDDTHR LP GH96-09938A P 4182134076/1 1000237676 1 09.07.2016 GH81-13601A U 4182134076 1000224921 1 05.06.2016 7086002211
Looking at the output, you would expect eight rows because you have three pairs of binary alternatives (PartCode - GH81-13601A or GH96-09938A; PS - P or U; SO_NO 1000224921 OR 1000237676). Because PartCode, PS, and SO_NO come from three different tables and the only inner join is on the service_order, you will get 2^3 rows. Without seeing the data, it is hard to say exactly what is wrong, but I assume when you say you are only expecting two rows, that these three elements are linked, although the tables are not. I suspect you need some foreign keys, but without some sample data, I cannot be sure.
OK having seen the input data I know what to do! In LP_Part_Codes get rid of your column serPluspart (it is not helping). Now add a SubOrder (smallint) to each of LP_Part_Codes, LP_PS_Codes and LP_Confirmation_Codes.
Next add a column to your unpivots e.g:
insert into LP_PS_Codes(Service_Order, PS, SubOrder)
select Service_order,PS,SubOrder
from LP_Pending_Jobs
cross apply (
select PS1 as PS, 1 AS SubOrder where len(PS1)>0
union all
select PS2, 2 AS SubOrder where len(PS2) > 0
union all
select PS3, 3 AS SubOrder where len(PS3) > 0
union all
select PS4, 4 AS SubOrder where len(PS4) > 0
union all
select PS5, 5 AS SubOrder where len(PS5) > 0
) unp;
Do the same for LP_Part_Codes and LP_Confirmation_Codes.
Now you can amend your main select by adding LP_Part_Codes.SubOrder = LP_PS_Codes.SubOrder = LP_Confirmation_Codes.SubOrder
And you will now have only two records for this order.
HTH
Jonathan

SQL order by included character and string

I have a table and i want to colum joint_no column. The column's values are like these
FW-1
FW-2
.
.
.
FW-13
FW-R1
FW-1A
When i ordered them i get this results
FW-1
FW-10
FW-11
FW-12
FW-13
FW-1A
.
.
FW-R1
I want to get this result after sql query
FW-1
FW-1A
FW-2
FW-3
..
FW-13
FW-R1
can anybody help me?
If you can do it, I'd advise you to renumber the values so that the 'logical' order sticks to the alphabetical order. F-1 will then be updated to F-01, or F-001.
If you cannot do it, add a field that will be populated with the 'ordered' form of your code. You 'll then be able to order by the F-001 column and still display the F-1 value
Otherwise ordering your records will rapidly become your nightmare.
Using Patindex to find the first numeric expression as first sort field, then extracting the numeric part as integer as second sortfield and using the whole string as third sort field you might get the desired result.
Declare #a Table (c varchar(50))
Insert Into #a
Select 'FW-1'
Union Select 'FW-10'
Union Select 'FW-11'
Union Select 'FW-12'
Union Select 'FW-13'
Union Select 'FW-1A'
Union Select 'FW-2'
Union Select 'FW-3'
Union Select 'FW-R1'
Union Select 'FW-A1'
;With CTE as
(Select 1 as ID
Union All
Select ID + 1 from CTE where ID < 100
)
Select * from
(
Select c
,PATINDEX('%[0-9]%',c) as s1
,(Select Cast(
(Select Case
When SUBSTRING(c, ID, 1) LIKE '[0-9]'
Then SUBSTRING(c, ID, 1)
Else ''
End
From (Select * from CTE) AS X(ID)
Where ID <= LEN(c)
For XML PATH(''))
as int)
)
as s2
from
#a
) x
order by
s1,s2,c
With the output:
FW-1 4 1 -1
FW-1A 4 1 -1A
FW-2 4 2 -2
FW-3 4 3 -3
FW-10 4 10 -10
FW-11 4 11 -11
FW-12 4 12 -12
FW-13 4 13 -13
FW-A1 5 1 A1
FW-R1 5 1 R1
If the leading part is not fixed (FW-) you might need to add one additional sort field
Declare #a Table (c varchar(50))
Insert Into #a
Select 'FW-1'
Union Select 'FW-10'
Union Select 'FW-11'
Union Select 'FW-12'
Union Select 'FW-13'
Union Select 'FW-1A'
Union Select 'FW-2'
Union Select 'FW-3'
Union Select 'FW-R1'
Union Select 'FW-A1'
Union Select 'AB-A1'
Union Select 'AB-11'
;With CTE as
(Select 1 as ID
Union All
Select ID + 1 from CTE where ID < 100
)
Select * from
(
Select c
,SubString(c,1,PATINDEX('%[0-9]%',c)-1) as S0
,PATINDEX('%[0-9]%',c) as s1
,(Select Cast(
(Select Case
When SUBSTRING(c, ID, 1) LIKE '[0-9]'
Then SUBSTRING(c, ID, 1)
Else ''
End
From (Select * from CTE) AS X(ID)
Where ID <= LEN(c)
For XML PATH(''))
as int)
)
as s2
from
#a
) x
order by
s0,s1,s2,c

Finding Covariance using SQL

# dt---------indx_nm1-----indx_val1-------indx_nm2------indx_val2
2009-06-08----ABQI------1001.2------------ACNACTR----------300.05
2009-06-09----ABQI------1002.12 ----------ACNACTR----------341.19
2009-06-10----ABQI------1011.4------------ACNACTR----------382.93
2009-06-11----ABQI------1015.43 ----------ACNACTR----------362.63
I have a table that looks like ^ (but with hundreds of rows that dates from 2009 to 2013). Is there a way that I could calculate the covariance : [(indx_val1 - avg(indx_val1)) * (indx_val2 - avg(indx_val2)] divided by total number of rows for each value of indx_val1 and indx_val2 (loop through the entire table) and return just a simple value for cov(ABQI, ACNACTR)
Since you have aggregates operating over two different groups, you will need two different queries. The main one groups by dt to get your row values per date. The other query has to perform AVG() and COUNT() aggregates across the whole rowset.
To use them both at the same time, you need to JOIN them together. But since there's no actual relation between the two queries, it is a cartesian product and we'll use a CROSS JOIN. Effectively, that joins every row of the main query with the single row retrieved by the aggregate query. You can then perform the arithmetic in the SELECT list, using values from both:
So, building on the query from your earlier question:
SELECT
indxs.*,
((indx_val2 - indx_val2_avg) * (indx_val1 - indx_val1_avg)) / total_rows AS cv
FROM (
SELECT
dt,
MAX(CASE WHEN indx_nm = 'ABQI' THEN indx_nm ELSE NULL END) AS indx_nm1,
MAX(CASE WHEN indx_nm = 'ABQI' THEN indx_val ELSE NULL END) AS indx_val1,
MAX(CASE WHEN indx_nm = 'ACNACTR' THEN indx_nm ELSE NULL END) AS indx_nm2,
MAX(CASE WHEN indx_nm = 'ACNACTR' THEN indx_val ELSE NULL END) AS indx_val2
FROM table1 a
GROUP BY dt
) indxs
CROSS JOIN (
/* Join against a query returning the AVG() and COUNT() across all rows */
SELECT
'ABQI' AS indx_nm1_aname,
AVG(CASE WHEN indx_nm = 'ABQI' THEN indx_val ELSE NULL END) AS indx_val1_avg,
'ACNACTR' AS indx_nm2_aname,
AVG(CASE WHEN indx_nm = 'ACNACTR' THEN indx_val ELSE NULL END) AS indx_val2_avg,
COUNT(*) AS total_rows
FROM table1 b
WHERE indx_nm IN ('ABQI','ACNACTR')
/* And it is a cartesian product */
) aggs
WHERE
indx_nm1 IS NOT NULL
AND indx_nm2 IS NOT NULL
ORDER BY dt
Here's a demo, building on your earlier one: http://sqlfiddle.com/#!6/2ec65/14
Here is a Scalar-valued function to perform a covariance calculation on any two column table formatted to XML.
To Test: Compile the function then execute the Alpha Test
CREATE Function [dbo].[Covariance](#XmlTwoValueSeries xml)
returns float
as
Begin
/*
-- -----------
-- ALPHA TEST
-- -----------
IF object_id('tempdb..#_201610101706') is not null DROP TABLE #_201610101706
select *
into #_201610101706
from
(
select *
from
(
SELECT '2016-01' Period, 1.24 col0, 2.20 col1
union
SELECT '2016-02' Period, 1.6 col0, 3.20 col1
union
SELECT '2016-03' Period, 1.0 col0, 2.77 col1
union
SELECT '2016-04' Period, 1.9 col0, 2.98 col1
) A
) A
DECLARE #XmlTwoValueSeries xml
SET #XmlTwoValueSeries = (
SELECT col0,col1 FROM #_201610101706
FOR
XML PATH('Output')
)
SELECT dbo.Covariance(#XmlTwoValueSeries) Covariance
*/
declare #returnvalue numeric(20,10)
set #returnvalue =
(
SELECT SUM((x - xAvg) *(y - yAvg)) / MAX(n) AS [COVAR(x,y)]
from
(
SELECT 1E * x x,
AVG(1E * x) OVER (PARTITION BY (SELECT NULL)) xAvg,
1E * y y,
AVG(1E * y) OVER (PARTITION BY (SELECT NULL)) yAvg,
COUNT(*) OVER (PARTITION BY (SELECT NULL)) n
FROM
(
SELECT
e.c.value('(col0/text())[1]', 'float' ) x,
e.c.value('(col1/text())[1]', 'FLOAT' ) y
FROM #XmlTwoValueSeries.nodes('Output') e(c)
) A
) A
)
return #returnvalue
end
GO