Use min function without grouping - tsql

How can I retrieve the min of a date without group by?
declare #table table
(
SaleDate date
)
insert into #table
select '7/8/2021' union
select '7/21/2021'
declare #dimdate table
(
fulldate date,
WeekNumberOfYear int
)
insert into #dimdate
select '7/4/2021', 28 union
select '7/5/2021', 28 union
select '7/6/2021', 28 union
select '7/7/2021', 28 union
select '7/8/2021', 28 union
select '7/9/2021', 28 union
select '7/10/2021', 28 union
select '7/11/2021', 29 union
select '7/18/2021', 30 union
select '7/19/2021', 30 union
select '7/20/2021', 30 union
select '7/21/2021', 30 union
select '7/22/2021', 30 union
select '7/23/2021', 30 union
select '7/24/2021', 30
select datepart(week, saledate) 'wk',
min(fulldate) as 'Beginning_Week'
from #table t inner join #dimdate d on
datepart(week, saledate) = WeekNumberOfYear
group by datepart(week, saledate), WeekNumberOfYear
How can I retrieve the same result as above without a group by?

Do you mean something like this?
select dt,
(select min(WeekNumberOfYear) from DimDate) as minWeekNumberOfYear
from #table

Just use a windowed aggregate:
SELECT dt,
MIN(dt) OVER () AS MinDt
FROM #table;

You can use MIN() window function if you partition by WeekNumberOfYear and use DISTINCT in the SELECT statement so that there are no duplicates:
SELECT DISTINCT
d.WeekNumberOfYear wk,
MIN(fulldate) OVER (PARTITION BY d.WeekNumberOfYear) Beginning_Week
FROM #table t INNER JOIN #dimdate d
ON DATEPART(week, t.saledate) = d.WeekNumberOfYear;
See the demo.

Related

Join 2 Alias Table from Union Select Table with same numbers of row

I hava 2 table from alias table result of select and union with have same number of row, how or can i make table 2 in right side of table 1? There are dont have same record
Thank you
First query:
SELECT * FROM (
SELECT COUNT(*) “DATA 220” FROM istros_sls_store.sales_store_220)—CEK
UNION ALL
SELECT COUNT(*) FROM istros_sls_item_sales_item_220)—CEK
UNION ALL
SELECT COUNT(*) FROM istros_sls_scat_sales_small_cat_220—CEK
UNION ALL
SELECT COUNT(*) FROM istros_inventory_hstr.inventory_hstr_dtl_220)—CEK
UNION ALL
SELECT COUNT(*) FROM istros_sos.stock_out_supplier_220—CEK
) a
Output from first query:
DATA 220
41
236633
11509
187174
1132
Second query:
SELECT * FROM (
SELECT COUNT(*) “DATA 226” FROM istros_sls_store.sales_store_226—CEK
UNION ALL
SELECT COUNT(*) FROM istros_sls_item_sales_item_226—CEK
UNION ALL
SELECT COUNT(*) FROM istros_sls_scat_sales_small_cat_226—CEK
UNION ALL
SELECT COUNT(*) FROM istros_inventory_hstr.inventory_hstr_dtl_226—CEK
UNION ALL
SELECT COUNT(*) FROM istros_sos.stock_out_supplier_226—CEK
) b
Output from second query:
DATA 226
41
243053
11437
193549
960
The desired output combines these two columns:
DATA 220 | DATA 226
41 | 41
236633 | 243053
11509 | 11437
187174 | 193549
1132 | 960
You may try simple selecting both counts in the union query, so that each one appears as a separate column in the output. Note that below I introduce a computed column, pos, which keeps track of which count queries should appear first in the result set.
SELECT "DATA 220", "DATA 226"
FROM
(
SELECT
1 AS pos,
(SELECT COUNT(*) FROM istros_sls_store.sales_store_220) AS “DATA 220”,
(SELECT COUNT(*) FROM istros_sls_store.sales_store_226) AS “DATA 226”
UNION ALL
SELECT
2,
(SELECT COUNT(*) FROM istros_sls_item_sales_item_220),
(SELECT COUNT(*) FROM istros_sls_item_sales_item_226)
UNION ALL
SELECT
3,
(SELECT COUNT(*) FROM istros_sls_scat_sales_small_cat_220),
(SELECT COUNT(*) FROM istros_sls_scat_sales_small_cat_226)
UNION ALL
SELECT
4,
(SELECT COUNT(*) FROM istros_inventory_hstr.inventory_hstr_dtl_220),
(SELECT COUNT(*) FROM istros_inventory_hstr.inventory_hstr_dtl_226)
UNION ALL
SELECT
5,
(SELECT COUNT(*) FROM istros_sos.stock_out_supplier_220),
(SELECT COUNT(*) FROM istros_sos.stock_out_supplier_226)
) t
ORDER BY
pos;

postgresql combining several periods into one

I'm trying to combine range.
WITH a AS (
select '2017-09-16 07:12:57' as begat,'2017-09-16 11:30:22' as endat
union
select '2017-09-18 17:05:21' ,'2017-09-19 13:18:01'
union
select '2017-09-19 15:34:40' ,'2017-09-22 13:29:37'
union
select '2017-09-22 12:24:16' ,'2017-09-22 13:18:29'
union
select '2017-09-28 09:48:54' ,'2017-09-28 13:39:13'
union
select '2017-09-20 13:52:43' ,'2017-09-20 14:14:43'
), b AS (
SELECT *, lag(endat) OVER (ORDER BY begat) < begat OR NULL AS step
FROM a
)
, c AS (
SELECT *, count(step) OVER (ORDER BY begat) AS grp
FROM b
)
SELECT min(begat), coalesce( max(endat), 'infinity' ) AS range
FROM c
GROUP BY grp
ORDER BY 1
Result
1 "2017-09-16 07:12:57";"2017-09-16 11:30:22"
2 "2017-09-18 17:05:21";"2017-09-19 13:18:01"
3 "2017-09-19 15:34:40";"2017-09-22 13:29:37"
4 "2017-09-22 12:24:16";"2017-09-22 13:18:29"
5 "2017-09-28 09:48:54";"2017-09-28 13:39:13"
positions 3,4 intersect (endata> next begat)
How do I make the union of all the intersections into one large interval
I need result
1 "2017-09-16 07:12:57";"2017-09-16 11:30:22"
2 "2017-09-18 17:05:21";"2017-09-19 13:18:01"
3 "2017-09-19 15:34:40";"2017-09-22 13:29:37"
4 "2017-09-28 09:48:54";"2017-09-28 13:39:13"
Hey I would suggest using the following process :
1- Identify when a row is new, so you give a value of 1 to values that do not overlap (CTE b)
2- Sequence together the rows that have overlaps with others. This way you can see have a common identifier that will allow you to MAX and MIN begat and endat (CTE c)
3- For each sequence, give the MIN of begat and the MAX of endat so you will have your final values
WITH a AS (
select '2017-09-16 07:12:57' as begat,'2017-09-16 11:30:22' as endat
union
select '2017-09-18 17:05:21' ,'2017-09-19 13:18:01'
union
select '2017-09-19 15:34:40' ,'2017-09-22 13:29:37'
union
select '2017-09-22 12:24:16' ,'2017-09-22 13:18:29'
union
select '2017-09-28 09:48:54' ,'2017-09-28 13:39:13'
union
select '2017-09-20 13:52:43' ,'2017-09-20 14:14:43'
)
, b AS (
SELECT
begat
, endat
, (begat > MAX(endat) OVER w IS TRUE)::INT is_new
FROM a
WINDOW w AS (ORDER BY begat ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING)
)
, c AS (
SELECT
begat
, endat
, SUM((is_new)) OVER (ORDER BY begat) seq
FROM b
)
SELECT
MIN(begat) beg_at
, MAX(endat) end_at
FROM c
GROUP BY seq
EDITED
If you need speed you can use a psql function:
create or replace function append_ranges_in_a() returns setof a
language plpgsql
as
$BODY$
declare
v_current a%rowtype;
v_new a%rowtype;
v_first boolean:=true;
begin
for v_current in select begat, endat from a order by begat, endat
loop
if v_first then
v_first := false;
v_new.begat := v_current.begat;
elsif v_new.endat < v_current.begat then
return next v_new;
v_new.begat := v_current.begat;
end if;
v_new.endat := greatest(v_current.endat,v_new.endat);
end loop;
return next v_new;
return;
end;
$BODY$;
select * from append_ranges_in_a()
I test it with ~ 400000 rows:
delete from a;
insert into a (begat, endat)
select time::text, (time+'1 day'::interval)::text
from (select t+(round(random()*23.0)||' hours')::interval as time
from generate_series('1401-01-01'::timestamp,'2018-08-21'::timestamp,'1 day'::interval) t
) t;
select count(*) from a;
select * from append_ranges_in_a() offset 100000 limit 10
and it is twice fast as O(n^2) pure SQL version.
OLD slow solution:
You can use a recursive WITH query https://www.postgresql.org/docs/current/static/queries-with.html to construct the result row by row.
I create the table
The first row is the candidate first row (ending where ending), but the row is not "ready"
Then I look at the next row (step) and if it is not intersecting I add a ready row,
Also I add a not ready row with the current (last) observed range
When I do not have more rows I calculate the last row
I retain ready rows and the last row
Here is the code
CREATE TABLE a as
select '2017-09-16 07:12:57' as begat,'2017-09-16 11:30:22' as endat
union
select '2017-09-18 17:05:21' ,'2017-09-19 13:18:01'
union
select '2017-09-19 15:34:40' ,'2017-09-22 13:29:37'
union
select '2017-09-22 12:24:16' ,'2017-09-22 13:18:29'
union
select '2017-09-28 09:48:54' ,'2017-09-28 13:39:13'
union
select '2017-09-20 13:52:43' ,'2017-09-20 14:14:43';
WITH RECURSIVE t(begat, endat, ready, step) AS (
select * from (
select *,false,1 from a order by begat, endat limit 1) a
UNION ALL
SELECT new_rows.*
FROM (SELECT * FROM t ORDER BY begat DESC limit 1) t,
lateral (SELECT * FROM a ORDER BY begat, endat OFFSET step LIMIT 1) a,
lateral (
SELECT t.begat, t.endat, true as ready, step WHERE t.endat < a.begat
UNION SELECT CASE WHEN t.endat < a.begat THEN a.begat ELSE t.begat END, greatest(a.endat, t.endat), false, step+1
) new_rows
)
select begat, endat
from (
select begat, endat, ready, row_number() over (order by begat desc, endat desc)=1 is_last
from t
order by begat, endat) t
where ready or is_last;
i using range type
https://www.postgresql.org/docs/9.3/static/rangetypes.html
WITH tmp AS (
-- preparation range type
select begat, coalesce( endat, 'infinity' ) as endAt, tsrange( begat, coalesce( endat, 'infinity' ) ) as rg
from (
select '2017-09-11 17:13:03'::timestamp as begat ,'2017-09-12 12:24:09'::timestamp as endat union
select '2017-09-19 15:34:40','2017-09-20 11:04:45' union
select '2017-09-20 08:32:00','2017-09-22 13:28:37' union
select '2017-09-20 13:52:43','2017-09-20 14:14:43' union
select '2017-09-21 12:24:16','2017-09-21 13:28:29' union
select '2017-09-22 12:24:16','2017-09-22 13:28:29' union
select '2017-09-22 12:34:16','2017-09-23 13:28:29' union
select '2017-09-22 12:25:16','2017-09-24 13:28:29' union
select '2017-09-28 09:48:54','2017-09-28 13:39:13' union
select '2017-09-28 14:22:16','2017-09-28 15:52:15' union
select '2017-10-05 12:17:45','2017-10-06 12:35:38' union
select '2017-10-06 16:20:44','2017-10-07 10:11:09' union
select '2017-10-07 20:38:32','2017-10-09 14:42:29' union
select '2017-10-12 18:22:14','2017-10-12 20:52:45'
) a
),a as (
-- group intersecting range
select l.*
from tmp l left join tmp r on l.begAt > r.begAt and r.rg #> l.rg
where r.begAt is null
),
b AS (
SELECT *, lag(endat) OVER (ORDER BY begat) < begat OR NULL AS step
FROM a
)
, c AS (
SELECT *, count(step) OVER (ORDER BY begat) AS grp
FROM b
)
SELECT min(begat), coalesce( max(endat), 'infinity' ) AS range
FROM c
GROUP BY grp
ORDER BY 1

How to select multi row not from table

I want select multi records for serial number, like :
select * from
(
select 11 as COMP_NO
union
select 12
union
select 13
union
select 14
union
select 15
) A
If I want to select 100 records or more, Is there a better way ?
I find another way to solve my problem, create a user-defined function:
CREATE FUNCTION [dbo].[SerialTable]
(
#BeginNo int
, #EndNo int
)
RETURNS TABLE
AS
RETURN
(
WITH MySerial(SNO, ENO)
AS(
SELECT #BeginNo AS SNO, #EndNo as ENO
UNION ALL
SELECT SNO+1, ENO
FROM MySerial
WHERE SNO<ENO
)
SELECT SNO
FROM MySerial
)
then can select 10 or more records easily
SELECT * FROM dbo.SerialTable(11, 20);
SELECT * FROM dbo.SerialTable(11, 100);

How to create a table with dates in sequence between range in Hive?

I'm trying to Create a table with column date, And I want to insert date in sequence between Range.
Here's what I have tried:
SET StartDate = '2009-01-01';
SET EndDate = '2016-06-31';
CREATE TABLE DateRangeTable(mydate DATE, qty INT);
INSERT INTO DateRangeTable VALUES (select a.Date, 0
from (
select current_date - 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
) AS a where a.Date between '2019-01-01' and '2016-06-30');
This is the similar one:
select date_add(t.f1, t.start_r - pe.i) as date_range from (select '2022-01-01' as f1,datediff('2022-01-07','2022-01-01') as start_r,0 as end_r) t lateral view posexplode(split(space(start_r - end_r),' ')) pe as i,s;
You do not need VALUES keyword when using INSERT ... SELECT.
Working example:
set hivevar:start_date=2009-01-01;
set hivevar:end_date=2016-06-31;
CREATE TABLE DateRangeTable(mydate DATE, qty INT);
with date_range as
(--this query generates date range
select date_add ('${hivevar:start_date}',s.i) as dt
from ( select posexplode(split(space(datediff('${hivevar:end_date}','${hivevar:start_date}')),' ')) as (i,x) ) s
)
INSERT INTO TABLE DateRangeTable
select d.dt, 0 qty
from date_range d
where d.dt between '2019-01-01' and '2016-06-30');

COUNT with DATE INTERVALS

I have this table called Table1 as follows:
UserID Date
1 01/01/09
1 14/01/09
1 25/01/09
1 01/02/09
1 15/02/09
2 02/02/09
2 15/02/09
I am trying to return a result that counts the number of times between the MIN(Date) and 30 days after the MIN(Date) which is DATEADD(day,30,MIN(DATE)). So it would look something like this:
UserID Count
1 3
2 2
This code below is wrong but it expresses what I am trying to achieve:
SELECT COUNT(1) AS Count
FROM Table1
GROUP BY UserID
WHERE Date BETWEEN MIN(Date) AND DATEADD(day,30,MIN(DATE))
SELECT a.UserID, COUNT(a.UserID) AS [Count]
FROM Table1 AS a
INNER JOIN
(
SELECT UserID, MIN([Date]) AS MinDate
FROM Table1
GROUP BY UserID
) AS b
ON a.UserID = b.UserID
WHERE [Date] BETWEEN MinDate AND DATEADD(day, 30, MinDate)
GROUP BY a.UserID
Try this
DECLARE #table TABLE(
UserID INT,
DDate DATETIME
)
INSERT INTO #table (UserID,DDate) SELECT 1, '01 Jan 2009'
INSERT INTO #table (UserID,DDate) SELECT 1, '14 Jan 2009'
INSERT INTO #table (UserID,DDate) SELECT 1, '25 Jan 2009'
INSERT INTO #table (UserID,DDate) SELECT 1, '01 Feb 2009'
INSERT INTO #table (UserID,DDate) SELECT 1, '15 Feb 2009'
INSERT INTO #table (UserID,DDate) SELECT 2, '02 Feb 2009'
INSERT INTO #table (UserID,DDate) SELECT 2, '15 Feb 2009'
SELECT t.UserID,
COUNT(t.UserID)
FROM #table t INNER JOIN
(
SELECT UserID,
MinDate,
DATEADD(dd, 30, MinDate) MinDataAdd30
FROM (
SELECT UserID,
MIN(DDate) MinDate
FROM #table
GROUP BY UserID
) MINDates
) DateRange ON t.UserID = DateRange.UserID
WHERE t.DDate BETWEEN DateRange.MinDate AND DateRange.MinDataAdd30
GROUP BY t.UserID
I think you'll need to use a subquery to get the minimum date. I've shown it below
as a separate query into a variable as I'd probably turn this into a table-valued function.
DECLARE #STARTDATE DATETIME
SELECT #STARTDATE = MIN(DATE) FROM Table1
SELECT COUNT(1) AS Count
FROM Table1
GROUP BY UserID
WHERE Date BETWEEN #STARTDATE AND DATEADD(day,30,#STARTDATE)
I would do it like this:
select a.UserID, count(case when DDate - MinDate <= 30 then 1 end) as Count
from (
select UserID, min(DDate) MinDate
from Table1
group by UserID
) a
inner join Table1 t on a.UserID = t.UserID
group by a.UserID