Can any of these queries be done in SQL?
SELECT dates FROM system
WHERE dates > 'January 5, 2010' AND dates < 'January 30, 2010'
SELECT number FROM system
WHERE number > 10 AND number < 20
I'd like to create a generate_series, and that's why I'm asking.
I assume you want to generate a recordset of arbitrary number of values, based on the first and last value in the series.
In PostgreSQL:
SELECT num
FROM generate_series (11, 19) num
In SQL Server:
WITH q (num) AS
(
SELECT 11
UNION ALL
SELECT num + 1
FROM q
WHERE num < 19
)
SELECT num
FROM q
OPTION (MAXRECURSION 0)
In Oracle:
SELECT level + 10 AS num
FROM dual
CONNECT BY
level < 10
In MySQL:
Sorry.
Sort of for dates...
Michael Valentine Jones from SQL Team has an AWESOME date function
Check it out here:
http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=61519
In Oracle
WITH
START_DATE AS
(
SELECT TO_CHAR(TO_DATE('JANUARY 5 2010','MONTH DD YYYY'),'J')
JULIAN FROM DUAL
),
END_DATE AS
(
SELECT TO_CHAR(TO_DATE('JANUARY 30 2010','MONTH DD YYYY'),'J')
JULIAN FROM DUAL
),
DAYS AS
(
SELECT END_DATE.JULIAN - START_DATE.JULIAN DIFF
FROM START_DATE, END_DATE
)
SELECT TO_CHAR(TO_DATE(N + START_DATE.JULIAN, 'J'), 'MONTH DD YYYY')
DESIRED_DATES
FROM
START_DATE,
(
SELECT LEVEL N
FROM DUAL, DAYS
CONNECT BY LEVEL < DAYS.DIFF
)
If you want to get the list of days, with a SQL like
select ... as days where date is between '2010-01-20' and '2010-01-24'
And return data like:
days
----------
2010-01-20
2010-01-21
2010-01-22
2010-01-23
2010-01-24
This solution uses no loops, procedures, or temp tables. The subquery generates dates for the last thousand days, and could be extended to go as far back or forward as you wish.
select a.Date
from (
select curdate() - INTERVAL (a.a + (10 * b.a) + (100 * c.a)) DAY as Date
from (select 0 as a union all 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) as a
cross join (select 0 as a union all 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) as b
cross join (select 0 as a union all 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) as c
) a
where a.Date between '2010-01-20' and '2010-01-24'
Output:
Date
----------
2010-01-24
2010-01-23
2010-01-22
2010-01-21
2010-01-20
Notes on Performance
Testing it out here, the performance is surprisingly good: the above query takes 0.0009 sec.
If we extend the subquery to generate approx. 100,000 numbers (and thus about 274 years worth of dates), it runs in 0.0458 sec.
Incidentally, this is a very portable technique that works with most databases with minor adjustments.
Not sure if this is what you're asking, but if you are wanting to select something not from a table, you can use 'DUAL'
select 1, 2, 3 from dual;
will return a row with 3 columns, contain those three digits.
Selecting from dual is useful for running functions. A function can be run with manual input instead of selecting something else into it. For example:
select some_func('First Parameter', 'Second parameter') from dual;
will return the results of some_func.
In SQL Server you can use the BETWEEN keyword.
Link:
http://msdn.microsoft.com/nl-be/library/ms187922(en-us).aspx
You can select a range by using WHERE and AND WHERE. I can't speak to performance, but its possible.
The simplest solution to this problem is a Tally or Numbers table. That is a table that simply stores a sequence of integers and/or dates
Create Table dbo.Tally (
NumericValue int not null Primary Key Clustered
, DateValue datetime NOT NULL
, Constraint UK_Tally_DateValue Unique ( DateValue )
)
GO
;With TallyItems
As (
Select 0 As Num
Union All
Select ROW_NUMBER() OVER ( Order By C1.object_id ) As Num
From sys.columns as c1
cross join sys.columns as c2
)
Insert dbo.Tally(NumericValue, DateValue)
Select Num, DateAdd(d, Num, '19000101')
From TallyItems
Where Num
Once you have that table populated, you never need touch it unless you want to expand it. I combined the dates and numbers into a single table but if you needed more numbers than dates, then you could break it into two tables. In addition, I arbitrarily filled the table with 100K rows but you could obviously add more. Every day between 1900-01-01 to 9999-12-31 takes about 434K rows. You probably won't need that many but even if you did, the storage is tiny.
Regardless, this is a common technique to solving many gaps and sequences problems. For example, your original queries all ran in less than tenth of a second. You can also use this sort of table to solve gaps problems like:
Select NumericValue
From dbo.Tally
Left Join MyTable
On Tally.NumericValue = MyTable.IdentityColumn
Where Tally.NumericValue Between SomeLowValue And SomeHighValue
Related
How can I create a temp table in a sql select statement which will hold list of alphanumeric numbers.
Select yearlyQuarters from temp;
2023Q1
2022Q4
2022Q3
2022Q2
2022Q1
2021Q4
2021Q3
2021Q2
2021Q1
......
I tried creating the temp data as
WITH t(n) AS (
SELECT 1900 from dual
UNION ALL
SELECT n+1 from t WHERE n < 3000)
SELECT * FROM t;
I am not sure how can I add the quarter details to the numbers.
You can cross-join the result with the quarters. For example:
WITH t(n) AS (
SELECT 2000 from dual
UNION ALL
SELECT n+1 from t WHERE n < 2003)
SELECT t.n || 'Q' || y.q
FROM t
cross join (
select 1 as q from dual
union all select 2 from dual
union all select 3 from dual
union all select 4 from dual
) y
Result:
T.N||'Q'||Y.Q
-------------
2000Q1
2000Q2
2000Q3
2000Q4
2001Q1
2001Q2
2001Q3
2001Q4
2002Q1
2002Q2
2002Q3
2002Q4
2003Q1
2003Q2
2003Q3
2003Q4
See example at db<>fiddle.
Start with the date 1900-01-01 and repeatedly add 3 months to get the next quarter and then use TO_CHAR to format it:
SELECT TO_CHAR(
ADD_MONTHS(DATE '1900-01-01', 3 * LEVEL - 3),
'YYYY"Q"Q'
) AS yearly_quarters
FROM DUAL
CONNECT BY ADD_MONTHS(DATE '1900-01-01', 3 * LEVEL - 3) <= SYSDATE;
fiddle
Is it possible to use e.g. in line like this
SELECT trunc(dateadd(month,mon,add_months(date_trunc('month', CURRENT_DATE), -9))) as dates
FROM months
instead of -9, I would like to have there maximum value of column created like this (so in this case 12):
with months as (
select 1 as mon union all select 2 union all select 3 union all select 4 union all
select 5 as mon union all select 6 union all select 7 union all select 8 union all
select 9 as mon union all select 10 union all select 11 union all select 12
Is it possible somehow in Redshift? I tried just like that
SELECT trunc(dateadd(month,mon,add_months(date_trunc('month', CURRENT_DATE), -MAX(mon))) as dates
FROM months
but it is not working as expected
Yes, an alternate way is
With data as
(Select row_number() over (order by 1) rn from
table)
Select datediff(month, max(rn), current_date)
from data;
You can take some bigger table having more entries as required and replace that tablename in WITH clause like you said max 9 in above case and just limit them to the count of your tables month entries
I'm building a very complex SQL query in Oracle using multiple CTEs, where latter expressions rely on values from former ones. However I find that the whole execution halts if one of the prior CTEs contains no data. For example:
WITH CTE1 AS
(
SELECT
PEOPLE.ID AS PID,
APPLICATIONS.DATE AS APPDATE
FROM
PEOPLE,
APPLICATIONS
WHERE
APPLICATIONS.PERSON_ID = PEOPLE.ID
AND APPLICATIONS.DATE BETWEEN TO_DATE('2015-01-01','YYYY-MM-DD') AND TO_DATE('2015-01-31','YYYY-MM-DD')
),
CTE2 AS
(
SELECT
APPLICATIONS.PERSON_ID AS PID
MIN(APPLICATIONS.DATE) AS EARLIEST_APPDATE
FROM
CTE1,
APPLICATIONS
WHERE
APPLICATIONS.PERSON_ID = CTE1.PID
AND APPLICATIONS.DATE < ADD_MONTHS(CTE1.APPDATE, -18)
GROUP BY APPLICATIONS.PERSON_ID
),
MAIN_QUERY AS
(
SELECT
CTE1.PID AS PID
FROM
CTE1, CTE2
WHERE
-- Note that the PIDs should either match, or should not exist in CTE2
CTE1.PID = CTE2.PID OR (NOT EXISTS (SELECT PID FROM CTE2 WHERE CTE1.PID = CTE2.PID))
)
SELECT
MAIN_QUERY.PID
FROM MAIN_QUERY
Of course, I realise that the above example is completely pointless in itself, however I have just simplified this to illustrate the problem. CTE2 returns the earliest date of any application made by the same Person ID where the application is dated more than 18 months prior to the application date of CTE1. However... what if there are no such applications? CTE2 is capable of returning zero rows.
You will notice that CTE2 is not, in itself, referenced in the final query. An empty CTE2 is dealt with in MAIN_QUERY. So with regards the final query, it should not matter whether CTE2 actually returns any lines or not.
However the application I'm using (Business Objects) throws up an error that the query has "no data to fetch", when CTE2 has no lines.
I want to find a way around this, to enable my query to execute even if CTE2 returns null. Thanks.
CTE2 returns the earliest date of any application made by the same Person ID where the application is dated more than 18 months prior to the application date of CTE1. However... what if there are no such applications? CTE2 is capable of returning zero rows.
You can replace your sub-query factoring (WITH ... AS ( ... )) clauses with a simple analytical function:
Oracle Setup:
CREATE TABLE PEOPLE ( id ) AS
SELECT LEVEL FROM DUAL CONNECT BY LEVEL <= 3;
CREATE TABLE APPLICATIONS ( id, person_id, "DATE" ) AS
SELECT 1, 1, DATE '2015-01-01' FROM DUAL UNION ALL -- First row to return
SELECT 2, 1, DATE '2014-01-01' FROM DUAL UNION ALL -- Within 18 months
SELECT 3, 1, DATE '2013-01-01' FROM DUAL UNION ALL -- Before 18 months
SELECT 4, 1, DATE '2012-01-01' FROM DUAL UNION ALL -- Before 18 months and min
SELECT 5, 2, DATE '2015-01-02' FROM DUAL UNION ALL -- Second row to return
SELECT 6, 2, DATE '2014-01-02' FROM DUAL UNION ALL -- Within 18 months
SELECT 7, 3, DATE '2015-01-03' FROM DUAL UNION ALL -- Third row to return
SELECT 8, 3, DATE '2013-07-03' FROM DUAL; -- Exactly 18 months earlier
Query:
SELECT PID,
APPDATE,
CASE EARLIEST_APPDATE
WHEN APPDATE - INTERVAL '18' MONTH
THEN NULL
ELSE EARLIEST_APPDATE
END AS EARLIEST_APPDATE -- Included for the edge case where
-- EARLIEST_APPDATE is exactly 18 months
-- earlier as the RANGE BETWEEN is
-- inclusive.
FROM (
SELECT p.ID AS PID,
a."DATE" AS APPDATE,
MIN( a."DATE" ) OVER ( PARTITION BY p.ID
ORDER BY a."DATE"
RANGE BETWEEN UNBOUNDED PRECEDING
AND INTERVAL '18' MONTH PRECEDING )
AS EARLIEST_APPDATE
FROM PEOPLE p
INNER JOIN APPLICATIONS a
ON ( a.PERSON_ID = p.ID )
)
WHERE APPDATE BETWEEN DATE '2015-01-01' AND DATE '2015-01-31'
Output:
PID APPDATE EARLIEST_APPDATE
---------- ------------------- -------------------
1 2015-01-01 00:00:00 2012-01-01 00:00:00
2 2015-01-02 00:00:00
3 2015-01-03 00:00:00
I am trying to count (by hour) the number of entries into a database. I have successfully written a query that counts by hour, but it omits hours with zero entries. I need the results to include the zeroes. I have looked around the internet, and have found a lot of suggestions. I have created a View that has one column with datetime entries by minute. I've tried joining the main table to this view and I get the same results as without the join. Still no zeroes. Wondering how to get this query to return zeroes. I am using MS SQL 2008 R2. any suggestions?
declare #limit datetime;
use InputArchive
set #limit = current_timestamp;
set #limit = DATEADD(hour, -72, #limit);
SELECT DATEADD(hour, datediff(hour, 0, ArchivedItems.RecordCreated), 0) as TimeHour, COUNT(ISNULL((ArchivedItems.RecordCreated),' ')) as NumPerHour
FROM ArchivedItems
LEFT OUTER JOIN vw_hoursalot
ON vw_hoursalot.dtHr = ArchivedItems.RecordCreated
where InputTypeId = 5 or InputTypeId = 6 or InputTypeId = 8 and (ArchivedItems.RecordCreated >= #limit)
Group BY DATEADD(hour, Datediff(hour, 0, ArchivedItems.RecordCreated), 0)
order by DATEADD(hour, datediff(hour, 0, ArchivedItems.RecordCreated), 0) desc
option (MAXRECURSION 0)
Update: I changed the hoursalot view to be by the hour
I'm sorry but I'm not sure what you mean by full view SQL.
I can't put any strait info from the archivedItems table for legal reasons but the RecordCreated column is a strait timestamp ie '2013-04-05 14:09:59.167'
Try putting the vw_hoursalot as the leftmost table in the condition - this will mean that all rows from the view will be returned, whether a corresponding record in ArchivedHours is found or not.
I've edited again - this time I took the time to mock up some dummy data, and I realized that a problem in the code, both mine and yours, is that the ArchivedItems.RecordCreated column is being used as a filter in the WHERE condition. Therefore, only records with an existing RecordCreated value will be returned.
I've moved that condition to the JOIN, and run the query below against some very basic data I mocked up. Assuming that your vw_HoursALot view is returning twenty-four integers numbered 0-23, this ought to get you the data you are looking for.
PLEASE NOTE: I made the assumption that InputTypeID is from ArchivedItems.
WITH -- I used these two CTEs as my dummy data, based on the information in your post
vw_HoursALot AS
(
SELECT 1 dtHr UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION
SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION SELECT 10 UNION
SELECT 11 UNION SELECT 12 UNION SELECT 13 UNION SELECT 14 UNION SELECT 15 UNION
SELECT 16 UNION SELECT 17 UNION SELECT 18 UNION SELECT 19 UNION SELECT 20 UNION
SELECT 21 UNION SELECT 22 UNION SELECT 23 UNION SELECT 0
),
ArchivedItems AS
(
SELECT GETDATE() AS RecordCreated, 5 AS InputTypeID
UNION SELECT DATEADD(HOUR, -3, GETDATE()), 6 AS InputTypeID
)
-- this part is actually doing the work
SELECT
vw_HoursALot.dtHr,
COUNT(ArchivedItems.RecordCreated) AS NumPerHour
FROM
vw_hoursalot
LEFT OUTER JOIN
ArchivedItems ON
vw_hoursalot.dtHr = DATEPART(hour, ArchivedItems.RecordCreated) AND
ArchivedItems.RecordCreated >= DATEADD(hour, -72, GETDATE()) AND
(
InputTypeId = 5 OR
InputTypeId = 6 OR
InputTypeId = 8
)
GROUP BY vw_HoursALot.dtHr
ORDER BY vw_HoursALot.dtHr DESC
OPTION (MAXRECURSION 0)
Here is what I came up with:
declare #limit datetime;
declare #BaseTime datetime
set #BaseTime = '20141020 15:00'; --must be an even hour
set #limit = DATEADD(hour, -72, #BaseTime);
print #Basetime
;WITH
D1(N) AS (
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
),
D2(N) AS (SELECT 1 FROM D1 a, D1 b),
Numbers AS (SELECT TOP (100) ROW_NUMBER() OVER (ORDER BY (SELECT NULL))-1 AS Number FROM D2),
AllHours AS (SELECT DATEADD(hour,numbers.number,#limit) AS hr FROM Numbers),
Raw_Data AS (
SELECT DATEADD(hour,DATEDIFF(hour,#BaseTime,RecordCreated),#BaseTime) AS HourRecorded FROM
ArchivedItems
WHERE RecordCreated BETWEEN #limit AND #BaseTime
AND InputTypeID IN (5,6,8)
)
SELECT count(Raw_Data.HourRecorded),AllHours.hr
FROM AllHours left outer join Raw_Data on AllHours.hr = Raw_Data.HourRecorded
GROUP BY AllHours.hr
order by AllHours.hr
Here is what I used to create the test data
;WITH
D1(N) AS (
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
),
D2(N) AS (SELECT 1 FROM D1 a, D1 b),
D4(N) AS (SELECT 1 FROM D2 a, D2 b),
Numbers AS (SELECT TOP (10000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL))-1 AS Number FROM D4)
INSERT INTO ArchivedItems(InputTypeID, RecordCreated)
SELECT ABS(CAST(CAST(NEWID() AS VARBINARY) % 10 AS INT)), DATEADD(MINUTE, ABS(CAST(CAST(NEWID() AS VARBINARY) % 10000 AS INT)),'20141017')
FROM Numbers
--Make sure there is a gap
DELETE FROM ArchivedItems WHERE RecordCreated BETWEEN '2014-10-20 06:00:00.000' AND '2014-10-20 08:00:00.000'
I have this simple query:
Select
To_Date('2012-sep-03','yyyy-mon-dd')as Date_Of_Concern,
Count(Player_Id) as Retained
From Player
Where
(To_Date('2012-sep-03','yyyy-mon-dd')-Trunc(Init_Dtime))<=7
Which Results In:
Date_Of_Concern Retained
03-Sep-12 81319
This query counts all of the players in my database who have logged in(init_dtime) within 7 days of a specific date.
As it stands, I will have to run this query multiple times, for every "Day of Concern" that I wish to know about. Is there a better solution?
If you need to run this query for multiple dates, you would need some mean to hold more than one value. I suggest you use a NESTED TABLE object:
CREATE TYPE my_dates AS TABLE OF DATE;
/
SELECT d.column_value AS Date_Of_Concern, count(Player_Id) AS Retained
FROM Player
JOIN TABLE (my_dates(to_date('2012-sep-03', 'yyyy-mon-dd'),
to_date('2012-sep-04', 'yyyy-mon-dd'),
to_date('2012-sep-05', 'yyyy-mon-dd'))) d
ON d.column_value - trunc(Init_Dtime) BETWEEN 0 AND 7
GROUP BY d.column_value
Simply use GROUP BY to get the count by day:
Select
To_Date(Init_Dtime,'yyyy-mon-dd') as Date_Of_Concern,
Count(Player_Id) as Retained
From Player
Where
(To_Date('2012-sep-03','yyyy-mon-dd') - Trunc(Init_Dtime)) <= 7
GROUP BY To_Date(Init_Dtime,'yyyy-mon-dd')
ORDER BY To_Date(Init_Dtime,'yyyy-mon-dd')
within 7 days of a specific date
To be able to do what you want you will have to know what "specific date" you are talking about by either a formula or a date range. Any random date would obviously require the user to either enter that date or modify the query to run for that date (the way you mentioned).
Not sure if I understood you correctly but this is probably what you want. Might have suboptimal performance though.
12:32:22 HR#vm_xe> l
1 with player(id, dt) as (
2 select 1, date '2012-01-01' from dual union all
3 select 2, date '2012-01-01' from dual union all
4 select 3, date '2012-01-02' from dual union all
5 select 4, date '2012-01-03' from dual union all
6 select 5, date '2012-01-04' from dual union all
7 select 6, date '2012-01-05' from dual union all
8 select 7, date '2012-01-06' from dual union all
9 select 8, date '2012-01-07' from dual union all
10 select 9, date '2012-01-08' from dual union all
11 select 10, date '2012-01-09' from dual union all
12 select 11, date '2012-01-10' from dual
13 )
14 select distinct
15 to_char(dt, 'dd-mm-yyyy') dt
16 ,count(*) over (order by trunc(dt) range interval '7' day preceding) week_cnt
17 from player
18* order by 1, 2
12:32:22 HR#vm_xe> /
DT WEEK_CNT
---------- ----------
01-01-2012 2
02-01-2012 3
03-01-2012 4
04-01-2012 5
05-01-2012 6
06-01-2012 7
07-01-2012 8
08-01-2012 9
09-01-2012 8
10-01-2012 8
10 rows selected.
Elapsed: 00:00:00.01
p.s. do not code like
(To_Date('2012-sep-03','yyyy-mon-dd')-Trunc(Init_Dtime))<=7
code like
init_time between to_date('2012-SEP-03', 'yyyy-mon-dd') and to_date('2012-SEP-03', 'yyyy-mon-dd') + 7
Unless you don't care about indexes, of course :)