Repeat row number of times based on column value - sql

I have table which looks like this:
| name | start_date | duration_day|
========================================
| A | 2015-01-01 | 3 |
| B | 2015-01-02 | 2 |
And now I want to get an output like so:
| name | date |
=====================
| A | 2015-01-01 |
| A | 2015-01-02 |
| A | 2015-01-03 |
| B | 2015-01-02 |
| B | 2015-01-03 |
How can I do this in PostgreSQL?

Borrowing from Abelisto's answer, you can generate a series from the duration_day value with the generate_series() table function in the row source list. The function uses the duration_day value from my_table through an implicit lateral join.
SELECT name, start_date + n AS date
FROM my_table, generate_series(0, duration_day - 1) AS x(n);

select
name,
start_date + generate_series(0, duration_day - 1)
from
your_table;

Related

How do i get the latest user udpated column value in a table based on timestamp entry on a different table in SQL Server?

I have a temp table #StatusInfo with the following data
+---------+--------------+-------+-------------------------+--+
| OrderNo | GroupLineNum | Type1 | UpdateDate | |
+---------+--------------+-------+-------------------------+--+
| Order85 | NULL | 1 | 2019-11-25 05:15:55.000 | |
+---------+--------------+-------+-------------------------+--+
| Order86 | NULL | 1 | 2019-11-25 05:15:55.000 | |
+---------+--------------+-------+-------------------------+--+
| Order86 | 2 | 2 | 2019-11-25 05:32:23.773 | |
+---------+--------------+-------+-------------------------+--+
| Order87 | NULL | 1 | 2019-11-25 05:15:55.000 | |
+---------+--------------+-------+-------------------------+--+
| Order87 | 1 | 2 | 2019-11-25 05:43:37.637 | | B
+---------+--------------+-------+-------------------------+--+
| Order87 | 2 | 2 | 2019-11-25 05:42:32.390 | | A
+---------+--------------+-------+-------------------------+--+
| Order88 | NULL | 1 | 2019-11-25 06:35:13.000 | |
+---------+--------------+-------+-------------------------+--+
| Order88 | 1 | 2 | 2019-11-25 06:39:16.170 | |
+---------+--------------+-------+-------------------------+--+
Any update the user does on an order will be pulled into this temp table. Type 1 column with value 2 denotes a 'Required Date' field change by the user. The timestamp when the user made the change is the last column.
I have another temp table #LineInfo with the following data. This table is created by joining other tables and a left join with the above table too. The 'LineNum' column from below table will match the 'GroupLineNum' column in the above table for Type1=2
+---------+-----------+---------+------------+-------------------------+-------+
| OrderNo | RowNumber | LineNum | TotalCost | ReqDate | Type1 |
+---------+-----------+---------+------------+-------------------------+-------+
| Order85 | 1 | 1 | 309.110000 | 2019-10-30 23:59:00.000 | 1 |
+---------+-----------+---------+------------+-------------------------+-------+
| Order85 | 2 | 2 | 265.560000 | 2019-10-30 23:59:00.000 | 1 |
+---------+-----------+---------+------------+-------------------------+-------+
| Order86 | 1 | 1 | 309.110000 | 2019-10-30 23:59:00.000 | 1 |
+---------+-----------+---------+------------+-------------------------+-------+
| Order86 | 2 | 2 | 265.560000 | 2019-12-28 23:59:00.000 | 2 |
+---------+-----------+---------+------------+-------------------------+-------+
| Order87 | 1 | 1 | 309.110000 | 2020-01-31 23:59:00.000 | 2 |
+---------+-----------+---------+------------+-------------------------+-------+
| Order87 | 2 | 2 | 265.560000 | 2020-01-01 23:59:00.000 | 2 |
+---------+-----------+---------+------------+-------------------------+-------+
| Order88 | 1 | 1 | 309.110000 | 2019-11-29 23:59:00.000 | 2 |
+---------+-----------+---------+------------+-------------------------+-------+
| Order88 | 2 | 2 | 265.560000 | 2019-12-31 23:59:00.000 | 2 |
+---------+-----------+---------+------------+-------------------------+-------+
I will be joining #lineInfo with other tables to generate a new table with only one record for an orderno. Its grouped by orderno.
What I need to do is ensure that the new selectquery will have a column 'ReqDate' which will be the latest ReqDate value for the order.
For example, Order87 has two lines in the order. User updated Line 2 first at '2019-11-25 05:42:32.390' as seen in the row marked 'A' followed by Line 1 marked B # '2019-11-25 05:43:37.637 ' from the first table.
The new query should have the data from LineInfo and only the 'ReqDate' value matching the 'LineNum' that has the maximum of 'UpdateDate' column for Type1=2 and group by orderno.
So in our example, the output should have the ReqDate value '2020-01-31 23:59:00.000'.
In short, an order should have the most recently updated required date. Order can have multiple line items where reqdate is udpated. If there is no entry in #StatusInfo table with Type2 for an order, then any one of the ReqDate value from the #LineInfo table will suffice. Maybe the first line
I wrote something like this but it doesnt pull orders without any entry in StatusInfo table. Those orders will have a default value even though user didnt udpate and i am not sure how to join the result of this with LineInfo table to set the latest value
Select SIT.Orderno, max_date,grouplinenum
from #StatusInfo SIT
inner join
(SELECT Orderno, MAX(ActDate) as max_date
FROM #StatusInfo SI
WHERE SI.Type1=2
GROUP BY SI.Orderno)a
on a.Orderno = SIT.Orderno and a.max_date = SIT.ActDate
This is what I did. I created the blow CTE to load orders with req date change in order of Updated date and assigned it row number. Record with row number 1 will be the most recently updated date
;WITH cteLatestReqDate AS ( --We need to pull the latest ReqDate value the user set. So we are are ordering the SIT table by ActDate and assigning a row number and respective line's required date here
SELECT SIT.OrderNo, SIT.UpdateDate, SIT.GroupLineNum, LLI.ReqDate,
ROW_NUMBER() OVER (PARTITION BY SIT.OrderNo ORDER BY ActDate DESC) AS RowNum
FROM #StatusInfo SIT INNER JOIN #LineLevelInfo LLI ON SIT.OrderNo = OI.OrderNo AND SIT.GroupLineNum = LLI.LineNum
WHERE SIT.Type1 = 2
)
and then I added the below condition to my select query. Below select query is partial
SELECT
CASE WHEN MAX(LRD.ReqDate) IS NULL THEN CAST(FORMAT(MAX(LLI.ReqDate), 'yyMMdd') AS NVARCHAR(10))
ELSE CAST(FORMAT(MAX(LRD.ReqDate), 'yyMMdd') AS NVARCHAR(10)) END AS LatestReqDate
FROM #LineLevelInfo LLI
LEFT JOIN(SELECT * FROM cteLatestReqDate WHERE RowNum = 1)LRD ON LRD.OrderNo = LLI.OrderNo And LRD.GroupLineNum = LLI.LineNum

Conditionally apply date filter based on column - Oracle SQL

I have a table that looks like this:
| Type | DueDate |
|:----:|:---------:|
| A | 1/1/2019 |
| B | 2/3/2019 |
| C | NULL |
| A | 1/3/2019 |
| B | 9/1/2019 |
| C | NULL |
| A | 3/3/2019 |
| B | 4/3/2019 |
| C | NULL |
| B | 1/6/2019 |
| A | 1/19/2019 |
| B | 8/1/2019 |
| C | NULL |
What I need to accomplish is:
Grab all rows that have Type C. For any other type, only grab them if they have a due date AFTER May 1st 2019.
This is a dummy data -- in actuality, there are 10 or 15 types and about ~125M or so rows.
I have tried SELECT * FROM tblTest WHERE ((Type IN ('A', 'B') AND DueDate > '05-01-2019') OR Type = 'C') but that yields exactly the table above.
Simply changing WHERE DUEDATE >= '05/01/2019' filters outNULL`
How can I edit my WHERE statement to achieve desired results of below?
| Type | DueDate |
|:----:|:--------:|
| C | NULL |
| B | 9/1/2019 |
| C | NULL |
| C | NULL |
| B | 8/1/2019 |
| C | NULL |
SQL FIDDLE for reference
If your date were stored using the correct type, you would simply do:
select t.*
from tbltest
where duedate > date '2019-05-01' or type = 'C';
I would suggest you fix the duedate column to have the correct type. Until that is fixed, you can workaround the problem:
select t.*
from tbltest
where to_date(duedate, 'MM/DD/YYYY') > date '2019-05-01' or type = 'C';
As per the answer by gordon you need to use this in or condition.
If you have more conditions in where clause apart from what is mentioned in question, you need to group the conditions.
select *
from tbltest
where (duedate > DATE '2019-05-01'
or type = 'C') -- group these condition using brackets
And other_condition;
Actually your original query has or condition with all other conditions without any brackets and that yields all the rows in result.
Cheers!!

sql query to get selected records based on min (date) and string compare

I have the following table structures and data.
+------------+---------+----------+
| DateField | StringA | StringB |
+------------+---------+----------+
| 01/01/2015 | xxx | abc123 |
| 02/01/2015 | zzz | abc12345 |
| 03/01/2015 | xxx | abc |
| 04/01/2015 | xxx | abc |
| 05/01/2015 | xxx | abc |
+------------+---------+----------+
I will need to get the following type of records, is there a way to get it using 1 SQL statement?
Get all records that fulfill the following:
If the row data don't have the same value for StringA and StringB (example: first and second rows) AND
If row N StringA and StringB same as row N+1 StringA and StringB, only get the 1st row N and ignore the rest.
Basically the end result should be:
+------------+---------+----------+
| DateField | StringA | StringB |
+------------+---------+----------+
| 01/01/2015 | xxx | abc123 |
| 02/01/2015 | zzz | abc12345 |
| 03/01/2015 | xxx | abc |
+------------+---------+----------+
What you need is a grouping on stringa, stringb, pulling the min date:
SELECT STRINGA, STRINGB, MIN(DATEFIELD) AS MIN_DATEFIELD
FROM TABLENAME
GROUP BY STRINGA, STRINGB;
The result set will include a MIN_DATEFIELD column which will contain the minimum date for the matching combination of stringa and stringb.
Use row function
with x as
(select datefield, stringA, StringB, Row_number() over (partition by stringA, StringB order by Datefield) row
)
select datefield,stringA,StringB from x where row = 1
I think you want whenever there is a change to stringA and stringB. I would use lag() for this:
with t as
select t.*,
lag(stringA) over (order by datefield) as prev_stringA,
lag(stringB) over (order by datefield) as prev_stringB
from t
)
select t.*
from t
where (stringA <> prev_stringA or prev_stringA is null) or
(stringB <> prev_stringB or prev_stringB is null);
You can efficiently implement this in Vertica by using conditional_change_event. So, if this is your source table:
SQL> select * from t3 order by dt ;
dt | stra | strb
------------+------+----------
2015-01-01 | xxx | abc123
2015-01-02 | zzz | abc12345
2015-01-03 | xxx | abc
2015-01-04 | xxx | abc
2015-01-05 | xxx | abc
You can run...
SQL> select
min(dt) as dt, min(stra) as stra, min(strb) as strb
from (
select
dt, stra, strb,
conditional_change_event(stra || strb) over(order by dt) as cce
from t3
) b
group by cce
order by 1 ;
min | stra | strb
------------+------+----------
2015-01-01 | xxx | abc123
2015-01-02 | zzz | abc12345
2015-01-03 | xxx | abc

Select a column's data in a group and place in new column across entire group

I would like to select a column's data in a group and place that data in its own column for each row of the group.
Specifically, if I've got a table that looks like this:
SELECT * FROM Jobtable
JOBNum | ReqDateTime | RowNum
-------+----------+------
M210 | 2015-01-08 17:01:56.000 | 1
M214 | 2015-01-12 17:46:09.000 | 1
M214 | 2015-01-16 20:19:43.000 | 2
M219 | 2015-01-27 15:05:16.000 | 1
M219 | 2015-01-28 02:01:13.000 | 2
I want to show this:
JOBNum | ReqDateTime | FirstDateInEachGroup | Row |
-------+-------------------------+-------------------------+-----+
M210 | 2015-01-08 17:01:56.000 | 2015-01-08 17:01:56.000 | 1 |
M214 | 2015-01-12 17:46:09.000 | 2015-01-12 17:46:09.000 | 1 |
M214 | 2015-01-16 20:19:43.000 | 2015-01-12 17:46:09.000 | 2 |
M219 | 2015-01-27 15:05:16.000 | 2015-01-27 15:05:16.000 | 1 |
M219 | 2015-01-28 02:01:13.000 | 2015-01-27 15:05:16.000 | 2 |
I want create another column "FirstDateInEachGroup" on the fly which consists of the ReqDateTime which is in each JobNum group with the Row = 1. In the above example you can see that where there are two JobNum's that are the same they have a different "Row", andthe FirstDateInEachGroup would contain the same data in each row with the same JobNum.
How do I do this without using a cursor or function?
Thank you!
I think you want something like this:
SELECT
JobNum, ReqDateTime, RowNum,
(select min(ReqDateTime) from JobTable jt2 where jt2.JobNum = jt1.JobNum group by JobNum) as FirstDateInEachGroup
FROM Jobtable jt1
Just use the min() window function:
SELECT JobNum, ReqDateTime,
MIN(ReqDateTime) OVER (PARTITION BY JobNum) as FirstDateInEachGroup,
RowNum
FROM Jobtable;

SQL output based on a date range

Given the following two table scenario, how would I go about outputting the commission percentage based on the date range:
Commission Percentages
| User ID | Start Date | End Date | Percentage
| -------- | ---------- | ----------- | ----------
| 1 | 11/11/2014 | 11/30/2014 | 10%
| 1 | 11/30/2014 | NULL | 20%
| 2 | 10/10/2014 | NULL | 15%
Sales
| User ID | Sale Date |
| -------- | ---------- |
| 1 | 11/24/2014 |
| 1 | 12/1/2014 |
| 2 | 12/30/2014 |
I would like to end up with a join between the two like so (a null value in the end date field represents present - and the dates will also include a time stamp):
| User ID | Sales Date | Start Date | End Date | Percentage
| -------- | ---------- | ---------- | ---------- | ----------
| 1 | 11/24/2014 | 11/11/2014 | 11/30/2014 | 10%
| 1 | 12/1/2014 | 11/30/2014 | NULL | 20%
| 2 | 12/30/2014 | 10/10/2014 | NULL | 15%
I am using SQL Server 2012
Thanks
Something like this might work for you, however you need to figure your date logic (i.e. whether it should be greater than, or greater than/equal to) depending on how your system works:
select S.UserID, S.SalesDate, C.StartDate, C.EndDate, C.Percentage
from Sales AS S
inner join Commission AS C
on C.UserID = S.UserID
AND S.SalesDate > C.StartDate
AND S.SalesDate <= coalesce(C.EndDate, S.SalesDate)
I'm assuming the end date is the first date the percentage does not apply based on the data. User ID 1 has a vector overlap.
SELECT s.User_ID,
s.Sales_Date,
cp.Start_Date,
cp.End_Date,
cp.Pecrcentage
FROM Commission_Percentages cp
INNER JOIN Sales s
ON s.User_ID = cp.User_ID
AND s.Sale_Date >= cp.Start_Date
AND (s.Sale_Date < cp.End_Date OR cp.End_Date IS NULL)