SQL query help using two WHERE clauses - sql

I have a table with data spanning about two weeks. I want to see the average for the first 7 days and then the next 8.
I have tried various JOINS with no luck. I am new to SQL so I am probably missing something simple.
Basically these queries work. How do I combine them?
select count(Field)/8
from TABLE
WHERE Publish_date >= '04/05/19'
select count(Field)/7
from TABLE
WHERE Publish_date < '04/05/19'

If you really need to combine them, then you can do sub-queries:
SELECT
(
SELECT SUM(Field)/8
FROM TABLE
WHERE Publish_date >= '04/05/19'
) as date1,
(
SELECT SUM(Field)/7
FROM TABLE
WHERE Publish_date < '04/05/19'
) as date2
Please note that you wish to use SUM instead of COUNT, because COUNT just get rows count, not it's values summed up.

I think you can try the AVG() function. If your dates are proper, count of days will be taken care automatically.
2 weeks => 14 days. How did you get 7+8 = 15 days ?
If you need two different rows -
;with t (val, dt) as (
select 183, getdate()-6 union all
select 183, getdate()-5 union all
select 183, getdate()-4 union all
select 183, getdate()-3 union all
select 183, getdate()-2 union all
select 183, getdate()-1 union all
select 183, getdate() union all --< cutoff date
select 183, getdate()+1 union all
select 183, getdate()+2 union all
select 183, getdate()+3 union all
select 183, getdate()+4 union all
select 183, getdate()+5 union all
select 183, getdate()+6 union all
select 20, getdate()+7
)
select 'first-half' , AVG(val) averg from t where dt < getdate()
union all
select 'second-half' , AVG(val) averg from t where dt >= getdate()

Just use UNION ALL between them like this:
select count(Field)/8 from TABLE WHERE Publish_date >= '04/05/19'
UNION ALL
select count(Field)/7 from TABLE WHERE Publish_date < '04/05/19'

Related

How can I use the LAG FUNCTION to show revenue this year vs last year in Snowflake with?

I would like to show the current revenue vs last's year's revenue in the same row per region per type. Example:For 2022-04-01, US, Type 1 --> REVENUE: 2456, REVENUE_LAST_YEAR: 4000
2021-04-01, US, Type 1 --> REVENUE: 4000, REVENUE_LAST_YEAR: 0
For some reason, the Lag formula in Snowflake is showing wrong values. Could someone please help ?
WITH
indata(dt,region,type,revenue) AS (
SELECT DATE '2021-04-01','US','Type 1',4000 UNION ALL SELECT DATE '2021-05-01','Europe','Type 2',5777
UNION ALL SELECT DATE '2021-06-01','US','Type 1',45433 UNION ALL SELECT DATE '2021-07-01','Europe','Type 2',8955
UNION ALL SELECT DATE '2021-08-01','US','Type 1',45777 UNION ALL SELECT DATE '2021-09-01','Asia','Type 1',7533
UNION ALL SELECT DATE '2021-10-01','US','Type 1',8866 UNION ALL SELECT DATE '2021-11-01','Asia','Type 2',5534
UNION ALL SELECT DATE '2021-12-01','US','Type 2',4000 UNION ALL SELECT DATE '2022-01-01','Asia','Type 1',7244
UNION ALL SELECT DATE '2022-02-01','US','Type 1',6678 UNION ALL SELECT DATE '2022-03-01','Asia','Type 1',5654
UNION ALL SELECT DATE '2022-04-01','US','Type 1',2456 UNION ALL SELECT DATE '2022-05-01','Asia','Type 1',4525
UNION ALL SELECT DATE '2022-06-01','US','Type 1',6677 UNION ALL SELECT DATE '2022-07-01','Asia','Type 1',6654
UNION ALL SELECT DATE '2022-08-01','US','Type 1',6677 UNION ALL SELECT DATE '2022-09-01','Asia','Type 2',5754
UNION ALL SELECT DATE '2022-10-01','US','Type 1',7744 UNION ALL SELECT DATE '2022-11-01','Asia','Type 2',5644
UNION ALL SELECT DATE '2022-12-01','Europe','Type 2',6775 UNION ALL SELECT DATE '2023-01-01','Asia','Type 2',6777
UNION ALL SELECT DATE '2023-02-01','Europe','Type 2',7755
)
SELECT indata.*,
lag(REVENUE, 1, 0) over (partition by region,type,revenue order by year(dt)) REVENUE_last_year
FROM indata
order by year(dt)
Partitioning by region, type and month-day:
SELECT indata.*,
LAG(REVENUE, 1, 0) over (partition by region,type, TO_VARCHAR(dt, 'mmdd')
order by dt) AS REVENUE_last_year
FROM indata
ORDER BY dt;
Output:

how to streamline a select query

Hi I need to streamline a select query that will yield an array of 108 results.
The current query looks like this:
SELECT (
SELECT ISNULL(Sum(Dollars), 0)
FROM TableX
WHERE Column3 = 1
AND eventdate BETWEEN #start
AND #end
AND datepart(mm, eventdate) = 1
)
column 3 values: 1, 4, 3, 14, 17, 10, 9, 13, 6
so essentially... 1-1,1-2,1-3 to 1-12 and then4-1,4-2,4-3 to 4-12, etc.
because I'm new to this stuff, I just copy and pasted the code and adjusted the values but it's now a 25,000 character query which is way too long.
Any help or pointers in how to loop these would be much appreciated.
Thanks
CREATE VIEW TableX (UNIQUEID,FILEKEY,EVENTDATE,GROUP1,GROUP2,GROUP3,PDN,RATE,HOURS,DOLLARS) AS
SELECT 167278 AS UNIQUEID,1 AS FILEKEY,'2014-07-21 00:00:00.000' AS EVENTDATE,1 AS GROUP1,2 AS GROUP2,1 AS GROUP3,14 AS PDN,0 AS RATE,3 AS HOURS,0 AS DOLLARS
UNION ALL
SELECT 167277,1,'2014-07-24 00:00:00.000',1,2,1,1,5,1,5
UNION ALL
SELECT 167276,1,'2014-07-25 00:00:00.000',1,4,1,1,5,1,5
UNION ALL
SELECT 167269,1,'2014-07-07 00:00:00.000',1,2,1,1,5,8,40
UNION ALL
SELECT 167007,1,'2014-07-04 00:00:00.000',1,1,1,4,1,8,8
UNION ALL
SELECT 167006,1,'2014-07-06 00:00:00.000',1,1,1,1,1,1,1
UNION ALL
SELECT 166932,1,'2014-05-26 00:00:00.000',1,1,1,4,1,8,8
UNION ALL
SELECT 166904,1,'2013-12-25 00:00:00.000',1,1,1,4,18.25,8,146
UNION ALL
SELECT 166903,1,'2014-01-01 00:00:00.000',1,1,1,4,18.25,8,146
UNION ALL
SELECT 166902,1,'2013-11-28 00:00:00.000',1,1,1,4,18.25,8,146;
This might meet your needs:
select column3
, datepart(mm, eventdate) monthNum
, isnull(sum(dollars)) dollarSum
from tablex
where eventDate between #start and #end
group by column3,
datepart(mm, eventdate) monthNum
Having said that, selecting the month but not the year is not a good idea.

How to convert partial dates in Oracle SQL

I have 2 date columns in 2 diff tables that I need to compare, both varchar2 type. Both columns have partial and full dates based on the data.
T1:
ID Partial_date1
1 19-DEC-2016
2 06-MAY-2015
3 2016
4
5 AUG-2016
6 16-NOV-2015 00:00
7 01-JAN-2016
T2:
ID Partial_date2
1 09-JAN-2016
2 2016
3 SEP-2015
4
5 23-MAR-2016 00:00
6 15-MAY-2015
7
I want to search for all records that have full dates (as it is not possible to convert partial dates), to select only the records with full dates, I have used length >10. Here is the SQL I wrote but does not seem to be working.
select t1.id from t1, t2
where t1.id =t2.id
and length(t1.partial_date1)>10
and length(t2.partial_date2)>10
and to_date(t1.partial_date1,'DD-MON-YYYY') > to_date(t2.partial_date2,'DD-MON-YYYY')
I either get an error - ORA-01830: date format picture ends before converting entire input string
or literal does not match format string.
What am I doing wrong? how do I get the right results?
It seems you consider a date complete when the string starts with DD-MMM-YYYY. You can use REGEXP_LIKE to find such rows:
where regexp_like(partial_date, '^[[:digit:]]{2}-[[:upper:]]{3}-[[:digit:]]{4}')
(You may want to adjust the pattern according to your needs, e.g. replace [[:upper:]] with [[:alpha:]].)
In order to convert a date containing a textual month you should use TO_DATE with a language parameter:
to_date(partial_date, 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE=AMERICAN')
A possible query:
select tt1.id
from
(
select
id,
to_date(substr(partial_date1, 1, 11), 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE=AMERICAN')
as dt
from t1
where regexp_like(partial_date1, '^[[:digit:]]{2}-[[:upper:]]{3}-[[:digit:]]{4}')
) tt1
join
(
select
id,
to_date(substr(partial_date2, 1, 11), 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE=AMERICAN')
as dt
from t2
where regexp_like(partial_date2, '^[[:digit:]]{2}-[[:upper:]]{3}-[[:digit:]]{4}')
) tt2 on tt2.id = tt1.id and tt2.dt < tt1.dt;
However, keep in mind that you are still dealing with strings. Conversion on "dates" like these would fail and crash your query:
30-FEB-2017
01-YAN-2017
99-XXX-9999
So depending on the data quality your best bet may be to write a PL/SQL function in order to catch conversion errors.
When you write condition in where clause oracle may use it as access predicate. And it function to_date(t1.partial_date1,'DD-MON-YYYY') is used to any row.
That's why you get an error.
I see two ways:
First way is use a subquery to get a shrunk dataset, substr only 10 symbols and then convert it
with t1(id,partial_date1) as
(
select 1,'19-DEC-2016' from dual union all
select 2,'06-MAY-2015' from dual union all
select 3, '2016' from dual union all
select 4,'' from dual union all
select 5,'AUG-2016' from dual union all
select 6,'16-NOV-2015 00:00' from dual union all
select 7, '01-JAN-2016' from dual
), t2(id,partial_date2) as
(
select 1,'09-JAN-2016' from dual union all
select 2,'2016' from dual union all
select 3,'SEP-2015' from dual union all
select 4,'' from dual union all
select 5,'23-MAR-2016 00:00' from dual union all
select 6,'15-MAY-2015' from dual union all
select 7,'' from dual
)
select *
from
(select
t1.id,
partial_date1,
partial_date2
from
t1, t2
where
t1.id =t2.id
and length(t1.partial_date1) > 10
and length(t2.partial_date2) > 10
and rownum > 0)
where
to_date(substr(partial_date1,1,10),'DD-MON-YYYY') > to_date(substr(partial_date2,1,10),'DD-MON-YYYY');
/
The second way is to explicitly convert any format to properly one
with t1(id,partial_date1) as (
select 1,'19-DEC-2016' from dual union all
select 2,'06-MAY-2015' from dual union all
select 3, '2016' from dual union all
select 4,'' from dual union all
select 5,'AUG-2016' from dual union all
select 6,'16-NOV-2015 00:00' from dual union all
select 7, '01-JAN-2016' from dual)
,t2(id,partial_date2) as (
select 1,'09-JAN-2016' from dual union all
select 2,'2016' from dual union all
select 3,'SEP-2015' from dual union all
select 4,'' from dual union all
select 5,'23-MAR-2016 00:00' from dual union all
select 6,'15-MAY-2015' from dual union all
select 7,'' from dual)
select * from(
select t1.id,
case
when regexp_like(t1.partial_date1,'\d{1,2}-\w{3}-\d{4} \d{1,2}:\d{2}') then to_date(t1.partial_date1,'dd-MON-yyyy HH24:MI')
when regexp_like(t1.partial_date1,'\d{1,2}-\w{3}-\d{4}') then to_date(t1.partial_date1,'dd-MON-yyyy')
when regexp_like(t1.partial_date1,'\w{3}-\d{4}') then to_date(t1.partial_date1,'MON-yyyy')
when regexp_like(t1.partial_date1,'\d{4}') then to_date(t1.partial_date1,'yyyy')
end as pd1,
case
when regexp_like(t2.partial_date2,'\d{1,2}-\w{3}-\d{4} \d{1,2}:\d{2}') then to_date(t2.partial_date2,'dd-MON-yyyy HH24:MI')
when regexp_like(t2.partial_date2,'\d{1,2}-\w{3}-\d{4}') then to_date(t2.partial_date2,'dd-MON-yyyy')
when regexp_like(t2.partial_date2,'\w{3}-\d{4}') then to_date(t2.partial_date2,'MON-yyyy')
when regexp_like(t2.partial_date2,'\d{4}') then to_date(t2.partial_date2,'yyyy')
end as pd2
from t1,t2
where t1.id = t2.id)
where pd1 > pd2
/
case
when PurchaseDate = '0' then NULL
when right(PurchaseDate, 4) = '0000' then convert(date, left(PurchaseDate,4) + '1231', 112)
when RIGHT(PurchaseDate, 2) = '00' then DATEADD(day,-1,DATEADD(month,cast(left(RIGHT(Purchasedate,4),2) AS INT),DATEADD(year,cast(LEFT(Purchasedate, 4) AS int)-1900,0)))
else convert(date, ltrim(rtrim(cast(PurchaseDate as varchar(50)))), 112)
end
Apologize. This is from my phone. This does a few things. It assumes different formats plus it counters for missing days and months.
Hope this helps.

Average payment delay - year to date

I need to create a query that will allow me to determine the average payment delay per customer and I will hate to update that query each week.
Therefore I already calculated the delay with the date of the document and the date of the payment.
What I need to do know is to have a graph that will show the average of these delays per week but taking into account the previous weeks:
Week 1: average of delays from week 1
Week 2: average of delays from week 1 to week 2
Week 3: average of delays from week 1 to week 3
and so on.
Today, we are week 11 so next week, I will need to have the result of week 12 automatically in my graph.
I already tried a graph using "Running Total in" in the settings of the pivot table
but this result shows on:
Which is not helping because it's making a sum of the delays...
Here is how my code looks like:
SELECT ch.HDOCNO,
ch.HDOCDATE,
ch.HYEAR,
week(ch.HDOCDATE)-1 as "Week",
ch.HMDATE as "Payment date",
AVG(ch.HMDATE-ch.HDOCDATE) as "Delay"
from AC_CHISTO ch
where ch.HFYEAR = '2016'
and ch.HMDATE IS NOT NULL
and UPPER(ch.HDBK) = 'VEN'
GROUP BY ch.HDOCNO, ch.HDOCDATE, ch.HYEAR, ch.HMDATE
Here is an example of the data that I need to use:
The "date" table that Steve mentions is a fairly straightforward way to solve this problem. The basic premise is to use a pre-computed table to store all possible weeks, making it easy for you to aggregate your results over valid weeks. I've included comments in the code below to identify the different parts that I had to include just to return some example results using the data you provided.
--Create table to store source data (not part of a final solution, only needed in this case to return example data).
DECLARE #AC_CHISTO TABLE
(
HDOCNO int,
HDOCDATE datetime,
HYEAR varchar(4),
HMDATE datetime,
HDBK varchar(3)
)
INSERT INTO #AC_CHISTO
SELECT 610474, '02/26/2016', '2016', '03/02/2016', 'VEN'
UNION ALL
SELECT 611727, '03/04/2016', '2016', '03/11/2016', 'VEN'
UNION ALL
SELECT 611728, '03/04/2016', '2016', '03/09/2016', 'VEN'
UNION ALL
SELECT 6133119, '03/11/2016', '2016', '03/15/2016', 'VEN'
UNION ALL
SELECT 613120, '03/11/2016', '2016', '03/15/2016', 'VEN'
UNION ALL
SELECT 601019, '01/07/2016', '2016', '01/29/2016', 'VEN'
UNION ALL
SELECT 603591, '01/21/2016', '2016', '02/29/2016', 'VEN'
UNION ALL
SELECT 600195, '01/04/2016', '2016', '01/21/2016', 'VEN'
UNION ALL
SELECT 600732, '01/06/2016', '2016', '01/21/2016', 'VEN'
UNION ALL
SELECT 601921, '01/13/2016', '2016', '01/28/2016', 'VEN'
UNION ALL
SELECT 602561, '01/18/2016', '2016', '01/28/2016', 'VEN'
UNION ALL
SELECT 603451, '01/21/2016', '2016', '02/11/2016', 'VEN'
--A table containing all weeks. In the final solution, this should be a real table prepopulated with 1 through 52, rather than a table variable.
DECLARE #AllWeeks TABLE
(
[Week] int
)
INSERT INTO #AllWeeks
SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9 UNION ALL SELECT 10 UNION ALL SELECT 11 UNION ALL SELECT 12 UNION ALL SELECT 13 UNION ALL SELECT 14 UNION ALL SELECT 15
--The actual solution begins here.
DECLARE #MaxWeek int
SELECT #MaxWeek = MAX(DATEPART(WK, ch.HDOCDATE)-1) FROM #AC_CHISTO ch
SELECT
aw.[Week],
AVG(results.[Delay]) AS AvgDelay
FROM #AllWeeks aw
INNER JOIN
(
SELECT ch.HDOCNO,
ch.HDOCDATE,
ch.HYEAR,
DATEPART(WK, ch.HDOCDATE)-1 as "Week",
ch.HMDATE as "PaymentDate",
DATEDIFF(DD, ch.HDOCDATE, ch.HMDATE) as "Delay"
FROM #AC_CHISTO ch
WHERE ch.HYEAR = '2016'
and ch.HMDATE IS NOT NULL
and UPPER(ch.HDBK) = 'VEN'
) results
ON aw.[Week] >= results.[Week]
WHERE aw.[Week] <= #MaxWeek
GROUP BY
aw.[Week]

Searching SQL table for two consecutive missing dates

I want to search through a SQL table and find two consecutive missing dates.
For example, person 1 inserts 'diary' entry on day 1 and day 2, misses day 3 and day 4, and enters an entry on day 5.
I am not posting code because I am not sure of how to do this at all.
Thanks!
This uses a LEVEL aggregate to build the list of calendar dates from the first entry to the last, then uses LAG() to check a given date with the previous date, and then checks that neither of those dates had an associated entry to find those two-day gaps:
With diary as (
select to_date('01/01/2016','dd/mm/yyyy') entry_dt from dual union all
select to_date('02/01/2016','dd/mm/yyyy') entry_dt from dual union all
select to_date('04/01/2016','dd/mm/yyyy') entry_dt from dual union all
--leave two day gap of 5th and 6th
select to_date('07/01/2016','dd/mm/yyyy') entry_dt from dual union all
select to_date('08/01/2016','dd/mm/yyyy') entry_dt from dual union all
select to_date('10/01/2016','dd/mm/yyyy') entry_dt from dual )
select calendar_dt -1, calendar_dt
FROM (
select calendar_dt, entry_dt, lag(entry_dt) over (order by calendar_dt) prev_entry_dt
from diary
RIGHT OUTER JOIN (select min(entry_dt) + lvl as calendar_dt
FROM diary
,(select level lvl
from dual connect by level < (select max(entry_dt) - min(entry_dt)+1 from diary))
group by lvl) ON calendar_dt = entry_dt
order by calendar_dt
)
where entry_dt is null and prev_entry_dt is null
returns:
CALENDAR_DT-1, CALENDAR_DT
05/01/2016, 06/01/2016
I am only doing the calendar building to simplify building all 2-day gaps, as if a person took three days off that would be two overlapping two-day gaps (day 1-2, and days 2-3). If you want a far simpler query that outputs the start and end point of any gap of two or more days, then the following works:
With diary as (
select to_date('01/01/2016','dd/mm/yyyy') entry_dt from dual union all
select to_date('02/01/2016','dd/mm/yyyy') entry_dt from dual union all
select to_date('04/01/2016','dd/mm/yyyy') entry_dt from dual union all
select to_date('07/01/2016','dd/mm/yyyy') entry_dt from dual union all
select to_date('08/01/2016','dd/mm/yyyy') entry_dt from dual union all
select to_date('10/01/2016','dd/mm/yyyy') entry_dt from dual )
select prev_entry_dt +1 gap_start, entry_dt -1 gap_end
FROM (
select entry_dt, lag(entry_dt) over (order by entry_dt) prev_entry_dt
from diary
order by entry_dt
) where entry_dt - prev_entry_dt > 2
My high level approach to this problem would be to select from a dynamic table of dates, using an integer counter to add or subtract from the current DateTime to get as many dates as you require into the future or past, then LEFT join your data table to this, order by date and select the first row, or N many rows which have a NULL join.
So your data ends up being
DATE ENTRY_ID
---- -----
2016-01-01 1
2016-01-02 2
2016-01-03 NULL
2016-01-04 3
2016-01-05 4
2016-01-06 NULL
2016-01-07 NULL
2016-01-08 NULL
And you can pick all of the values you need from this dataset
Try this your problem looks like similar to this :-
Declare #temp Table(id int identity(1,1) not null,CDate smalldatetime ,val int)
insert into #temp select '10/2/2012',1
insert into #temp select '10/3/2012',1
insert into #temp select '10/5/2012',1
insert into #temp select '10/7/2012',2
insert into #temp select '10/9/2012',2
insert into #temp select '10/10/2012',2
insert into #temp select '10/13/2012',2
insert into #temp select '10/15/2012',2
DECLARE #startDate DATE= '10/01/2012'
DECLARE #endDate DATE= '10/15/2012'
SELECT t.Id, X.[Date],Val = COALESCE(t.val,0)
FROM
(SELECT [Date] = DATEADD(Day,Number,#startDate)
FROM master..spt_values
WHERE Type='P'
AND DATEADD(day,Number,#startDate) <= #endDate)X
LEFT JOIN #temp t
ON X.[Date] = t.CDate
Alternative you can try this :-
WITH dates AS (
SELECT CAST('2009-01-01' AS DATETIME) 'date'
UNION ALL
SELECT DATEADD(dd, 1, t.date)
FROM dates t
WHERE DATEADD(dd, 1, t.date) <= '2009-02-01')
SELECT t.eventid, d.date
FROM dates d
JOIN TABLE t ON d.date BETWEEN t.startdate AND t.enddate