Semivariance of variable - sql

I'm new with sql and I struggle with such a problem. Let's suppose I have a table like this:
Date Value
2014-01-01 1248.56
2014-01-02 1247.24
2014-01-03 1245.82
2014-01-04 1252.07
...
All I want to do is count semivariance of variable 'Value'.
Semivariance only takes into account those records which are less than the average of the sample. So basically it is just a transformartion of simply variance.
Any help would be appreciated!

you can try something like this
SELECT COUNT(*)
FROM
Table1
WHERE Value < (SELECT AVG(Value) FROM Table1)

If you need avg value then you can use such code.
CREATE TABLE #test
(
date DATE,
value NUMERIC(10,2)
)
INSERT INTO #test VALUES ('2014-01-01' , 1248.56 ),
('2014-01-02' , 1247.24),
('2014-01-03' , 1245.82),
('2014-01-04' , 1252.07);
SELECT * FROM #test a
CROSS JOIN (SELECT AVG(value) avg_value FROM #test) b
WHERE a.value < b.avg_value

Related

Compare a single-column row-set with another single-column row set in Oracle SQL

Is there any Oracle SQL operator or function, which compares 2 result sets whether they are the exact same or not. Currently my idea is to use MINUS operator in both directions, but I am looking for a better and performanter solution to achieve. The one result set is fixed (see below), the other depends on the records.
Very important: I am not allowed to change the schema and structure. So CREATE TABLE and CREATE TYPE etc. are not allowed here for me. Also important that oracle11g version is used where the solution must be found.
The shema for SQL Fiddle is:
CREATE TABLE DETAILS (ID INT, MAIN_ID INT, VALUE INT);
INSERT INTO DETAILS VALUES (1,1,1);
INSERT INTO DETAILS VALUES (2,1,2);
INSERT INTO DETAILS VALUES (3,1,3);
INSERT INTO DETAILS VALUES (4,1,4);
INSERT INTO DETAILS VALUES (5,2,1);
INSERT INTO DETAILS VALUES (6,2,2);
INSERT INTO DETAILS VALUES (7,3,1);
INSERT INTO DETAILS VALUES (7,3,2);
Now this is my SQL query for doing the job well (selects MAIN_IDs of those, whose 'VALUE's are exactly the same as the given lists'):
SELECT DISTINCT D.MAIN_ID FROM DETAILS D WHERE NOT EXISTS
(SELECT VALUE FROM DETAILS WHERE MAIN_ID=D.MAIN_ID
MINUS
SELECT * FROM TABLE(SYS.ODCINUMBERLIST(1, 2)))
AND NOT EXISTS
(SELECT * FROM TABLE(SYS.ODCINUMBERLIST(1, 2))
MINUS
SELECT VALUE FROM DETAILS WHERE MAIN_ID=D.MAIN_ID)
The SQL Fiddle link: http://sqlfiddle.com/#!4/25dde/7/0
If you use a collection (rather than a VARRAY) then you can aggregate the values into a collection and directly compare two collections:
CREATE TYPE int_list AS TABLE OF INT;
Then:
SELECT main_id
FROM details
GROUP BY main_id
HAVING CAST( COLLECT( value ) AS int_list ) = int_list( 1, 2 );
Outputs:
| MAIN_ID |
| ------: |
| 2 |
| 3 |
db<>fiddle here
Update
Based on your expanded fiddle in comments, you can use:
SELECT B.ID
FROM BUSINESS_DATA B
INNER JOIN BUSINESS_NAME N
ON ( B.NAME_ID=N.ID )
WHERE N.NAME='B1'
AND EXISTS (
SELECT business_id
FROM ORDERS O
LEFT OUTER JOIN TABLE(
SYS.ODCIDATELIST( DATE '2021-01-03', DATE '2020-04-07', DATE '2020-05-07' )
) d
ON ( o.orderdate = d.COLUMN_VALUE )
WHERE O.BUSINESS_ID=B.ID
GROUP BY business_id
HAVING COUNT( CASE WHEN d.COLUMN_VALUE IS NULL THEN 1 END ) = 0
AND COUNT( DISTINCT o.orderdate )
= ( SELECT COUNT(DISTINCT COLUMN_VALUE) FROM TABLE( SYS.ODCIDATELIST( DATE '2021-01-03', DATE '2020-04-07', DATE '2020-05-07' ) ) )
)
(Note: Do not implicitly create dates from strings; it will cause the query to fail, without there being any changes to the query text, if a user changes their NLS_DATE_FORMAT session parameter. Instead use TO_DATE with an appropriate format model or a DATE literal.)
db<>fiddle here

Select min date record from duplicates in table

Let say that I have this table "contract" which have duplicated records in the "END" column for the same ID.
ID
Begin
End
20
2016-01-01
9999-12-31
20
2020-01-01
9999-12-31
30
2018-01-01
2019-02-28
30
2019-03-01
9999-12-31
30
2020-02-01
9999-12-31
10
2019-01-01
2019-06-30
10
2019-07-01
2020-02-29
10
2020-03-01
9999-12-31
I want to get the oldest date in the "Begin" column for all the ID's that have duplicated records in the "END" column with the date "9999-12-31". So for this example I expect to get:
ID
Begin
20
2016-01-01
30
2019-03-01
I made an SQL script, but there should be a better way.
select ID, MIN(Begin) from
(
select * from contract m where exists
(
select 1 from contract v where END = '9999-12-31' and v.ID = m.ID
having count(ID)=2
)
and END = '9999-12-31'
)a
group by FUN_ID
If it is a big table, you really want to use EXISTS for finding duplicates because it will short circuit. Here's two ways to use EXISTS that might help with what you are trying to do.
DROP TABLE IF EXISTS #Test;
CREATE TABLE #Test
(
ID INT NOT NULL
,[Begin] DATE NOT NULL
,[End] DATE NOT NULL
)
;
INSERT INTO #Test
VALUES
(20,'2016-01-01','9999-12-31')
,(20,'2020-01-01','9999-12-31')
,(30,'2018-01-01','2019-02-28')
,(30,'2019-03-01','9999-12-31')
,(30,'2020-02-01','9999-12-31')
,(10,'2019-01-01','2019-06-30')
,(10,'2019-07-01','2020-02-29')
,(10,'2020-03-01','9999-12-31')
;
--See all duplicates with OldestBegin for context
SELECT
TST.ID
,TST.[Begin]
,TST.[End]
,OldestBegin = MIN([Begin]) OVER (PARTITION BY TST.ID,TST.[End])
FROM #Test AS TST
WHERE EXISTS
(
SELECT 1
FROM #Test AS _TST
WHERE TST.ID = _TST.ID
AND TST.[End] = _TST.[End]
AND TST.[Begin] <> _TST.[Begin]
)
;
--Get only oldest duplicate
SELECT
TST.ID
,TST.[End]
,[Begin] = MIN([Begin])
FROM #Test AS TST
WHERE EXISTS
(
SELECT 1
FROM #Test AS _TST
WHERE TST.ID = _TST.ID
AND TST.[End] = _TST.[End]
AND TST.[Begin] <> _TST.[Begin]
)
GROUP BY
TST.ID
,TST.[End]
;
Perhaps this will help:
DECLARE #Tab TABLE(ID INT,[Begin] DATE,[End] DATE)
INSERT #Tab
VALUES
(20,'2016-01-01','9999-12-31')
,(20,'2020-01-01','9999-12-31')
,(30,'2018-01-01','2019-02-28')
,(30,'2019-03-01','9999-12-31')
,(30,'2020-02-01','9999-12-31')
,(10,'2019-01-01','2019-06-30')
,(10,'2019-07-01','2020-02-29')
,(10,'2020-03-01','9999-12-31')
;WITH cte AS(
SELECT *
FROM #Tab
WHERE [End] = '9999-12-31'
)
SELECT ID, MIN([Begin]) AS [Begin]
FROM cte
GROUP BY ID
HAVING COUNT(*) > 1
Try this:
WITH test as (SELECT
count(*), min(begin) as Begin, ID from contract
where end = '9999-12-31' group by ID having count(*) > 1) select ID, Begin from test

UNPIVOT Holiday Hours

I have a table, that keeps track of store holiday hours:
LOCATION_ID DATE1 TIMES1 DATE2 TIMES2
123456 2020-12-12 10:00AM-09:00PM 2020-12-19 10:00AM-09:00PM
This is a highly oversimplified table. There's about 30 columns horzontially consisting of store operating hours by date - It continues (DATE3, TIMES3, DATE4, TIMES4, etc).
I need to unpivot the values vertically, ensuring the date and time values are on the same record.
(NOTE: Once I figure out to structure the UNPIVOT expression properly, I will use Dynamic SQL on my own to pivot the column names)
Desired Outcome:
LOCATION_ID DATE TIME
123456 2020-12-12 10:00AM-09:00PM
123456 2020-12-19 10:00AM-09:00PM
I tried using UNPIVOT, but I'm stuck. Any ideas?
SAMPLE DATA:
CREATE TABLE #HOURS (LOCATION_ID int, DATE1 varchar(255), TIMES1 varchar(255), DATE2
varchar(255), TIMES2 varchar(255));
INSERT INTO #HOURS VALUES ('123456', '2020-12-12', '10:00AM-09:00PM','2020-12-19','10:00AM-09:00PM' )
Code that I tried:
SELECT *
FROM (SELECT location_id,
[date1],
[times1],
[date2]
FROM #hours) AS cp
UNPIVOT ( pivotvalues
FOR pivvalues IN ([Date1],
[date2],
[times1]) ) AS up1
Gordon is 100% correct (+1).
However, if you are looking for a dynamic approach WITHOUT having to use Dynamic SQL, consider the following.
Example
Select Location_ID
,Date = max(case when [Item] like 'DATE%' then Value end)
,Time = max(case when [Item] like 'TIME%' then Value end)
From (
select A.Location_ID
,Grp = replace(replace([Item],'DATE',''),'TIMES','')
,B.*
from #hours A
Cross Apply [dbo].[tvf-XML-Unpivot-Row]( (Select A.* for XML RAW) ) B
Where [Item] not in ('LOCATION_ID')
) A
Group By Location_ID,Grp
Returns
Location_ID Date Time
123456 2020-12-12 10:00AM-09:00PM
123456 2020-12-19 10:00AM-09:00PM
The Table-Valued Function if Interested
CREATE FUNCTION [dbo].[tvf-XML-UnPivot-Row](#XML xml)
Returns Table
As
Return (
Select Item = xAttr.value('local-name(.)', 'varchar(100)')
,Value = xAttr.value('.','varchar(max)')
From #XML.nodes('//#*') xNode(xAttr)
)
Don't use unpivot. Use apply:
select h.location_id, v.date, v.time
from #hours h cross apply
(values (h.date1, h.times1), (h.date2, h.times2)
) v(date, time);
unpivot is non-standard syntax that does exactly one thing. APPLY is the SQL Server implementation of lateral joins. This is a very powerful join type -- using it for unpivoting is a good way to start learning the syntax.

How to select values by date field (not as simple as it sounds)

I have a table called tblMK The table contains a date time field.
What I wish to do is create a query which will each time, select the 2 latest entries (by the datetime column) and then get the date difference between them and show only that.
How would I go around creating this expression. This doesn't necessarily need to be a query, it could be a view/function/procedure or what ever works. I have created a function called getdatediff which receives to dates, and returns a string the says (x days y hours z minutes) basically that will be the calculated field. So how would I go around doing this?
Edit: I need to each time select 2 and 2 and so on until the oldest one. There will always be an even amount of rows.
Use only sql like this:
create table t1(c1 integer, dt datetime);
insert into t1 values
(1, getdate()),
(2, dateadd(day,1,getdate())),
(3, dateadd(day,2,getdate()));
with temp as (select top 2 dt
from t1
order by dt desc)
select datediff(day,min(dt),max(dt)) as diff_of_dates
from temp;
sql fiddle
On MySQL use limit clause
select max(a.updated_at)-min(a.updated_at)
From
( select * from mytable order by updated_at desc limit 2 ) a
Thanks guys I found the solution please ignore the additional columns they are for my db:
; with numbered as (
Select part,taarich,hulia,mesirakabala,
rowno = row_number() OVER (Partition by parit order.by taarich)
From tblMK)
Select a.rowno-1,a.part, a.Julia,b.taarich,as.taarich_kabala,a.taarich, a.mesirakabala,getdatediff(b.taarich,a.taarich) as due
From numbered a
Left join numbered b ON b.parit=a.parit
And b.rowno = a.rowno - 1
Where b.taarich is not null
Order by part,taarich
Sorry about mistakes I might of made, I'm on my smartphone.

How to select info from row above?

I want to add a column to my table that is like the following:
This is just an example of how the table is structured, the real table is more than 10.000 rows.
No_ Name Account_Type Subgroup (New_Column)
100 Sales 3
200 Underwear 0 250 *100
300 Bikes 0 250 *100
400 Profit 3
500 Cash 0 450 *400
So for every time there is a value in 'Subgroup' I want the (New_Column) to get the value [No_] from the row above
No_ Name Account_Type Subgroup (New_Column)
100 Sales 3
150 TotalSales 3
200 Underwear 0 250 *150
300 Bikes 0 250 *150
400 Profit 3
500 Cash 0 450 *400
There are cases where the table is like the above, where two "Headers" are above. And in that case I also want the first above row (150) in this case.
Is this a case for a cursor or what do you recommend?
The data is ordered by No_
--EDIT--
Starting from the first line and then running through the whole table:
Is there a way I can store the value for [No_] where [Subgroup] is ''?
And following that insert this [No_] value in the (New_Column) in each row below having value in the [Subgroup] row.
And when the [Subgroup] row is empty the process will keep going, inserting the next [No_] value in (New_Column), that is if the next line has a value in [Subgroup]
Here is a better image for what I´m trying to do:
SQL Server 2012 suggests using Window Offset Functions.
In this case : LAG
Something like this:
SELECT [No_]
,[Name]
,[Account_Type]
,[Subgroup]
,LAG([No_]) OVER(PARTITION BY [Subgroup]
ORDER BY [No_]) as [PrevValue]
FROM table
Here is an example from MS:
http://technet.microsoft.com/en-us/library/hh231256.aspx
The ROW_NUMBER function will allow you to find out what number the row is, but because it is a windowed function, you will have to use a common table expression (CTE) to join the table with itself.
WITH cte AS
(
SELECT [No_], Name, Account_Type, Subgroup, [Row] = ROW_NUMBER() OVER (ORDER BY [No_])
FROM table
)
SELECT t1.*, t2.[No_]
FROM cte t1
LEFT JOIN cte t2 ON t1.Row = t2.Row - 1
Hope this helps.
Next query will return Name of the parent row instead of the row itself, i.e. Sales for both Sales, Underwear, Bikes; and Profit for Profit, Cash:
select ISNULL(t2.Name, t1.Name)
from table t1
left join table t2 on t1.NewColumn = t2.No
So in SQL Server 2008 i created test table with 3 values in it:
create table #ttable
(
id int primary key identity,
number int,
number_prev int
)
Go
Insert Into #ttable (number)
Output inserted.id
Values (10), (20), (30);
Insert in table, that does what you need (at least if understood correctly) looks like this:
declare #new_value int;
set #new_value = 13; -- NEW value
Insert Into #ttable (number, number_prev)
Values (#new_value,
(Select Max(number) From #ttable t Where t.number < #new_value))
[This part added] And to work with subgroup- just modify the inner select to filter out it:
Select Max(number) From #ttable t
Where t.number < #new_value And Subgroup != #Subgroup
SELECT
No_
, Name
, Account_Type
, Subgroup
, ( SELECT MAX(above.No_)
FROM TableX AS above
WHERE above.No_ < a.No_
AND above.Account_Type = 3
AND a.Account_Type <> 3
) AS NewColumn
FROM
TableX AS a