Loop through dates - sql

We have a table to keep track of items in a warehouse:
-------------------------
ID DATEIN DATEOUT
-------------------------
What we need is a list of the amount of items in the warehouse per week.
This is how far we've gotten:
SELECT
COUNT (ID)
FROM
table
WHERE
table.DATEIN<=#somedate1#
AND
(
table.DATEOUT>#somedate1#
OR
table.DATEOUT IS NULL
)
This gives us the amount of items in the warehouse on date #somedate1#. However, we need a list of the amount per week. (keeping in mind that it's possible that some items stay in the warehouse for months)
The tricky part isn't the week, we can use DATEPART ("ww", #somedate1#), but looping through the dates.
Any ideas?

Have you tried?
SELECT
DATEPART("ww","D"),COUNT (ID)
FROM
(
SELECT Table.DATEIN as D
FROM table
WHERE table.DATEIN<=#somedate1# AND ( table.DATEOUT>#somedate1# OR table.DATEOUT IS NULL)
UNION ALL
SELECT Table.DATEOUT as D
FROM table
WHERE table.DATEIN<=#somedate1# AND ( table.DATEOUT>#somedate1# OR table.DATEOUT IS NULL)
)
GROUP BY DATEPART("ww","D")
First you need to make an UNION to make sure you have all DATEIN and DATEOUT, then you can GROUP BY

Here's what we've come up with so far.
This code gives a list of all dates where an item came into the warehouse or left. These are the dates we need to apply the code in my first post to. I.e. we need to loop through these dates where for each iteration the next date is used as #somedate1#
SELECT
table.DATEIN AS DATE
FROM
table
GROUP BY
table.DATEIN
UNION
SELECT
table.DATEOUT AS DATE
FROM
table
WHERE
table.DATEOUT NOT IN (
SELECT
table.DATEIN
FROM
table
GROUP BY
table.DATEIN
)
GROUP BY
table.DATEOUT
We're stuck at how we can loop through them though. Everything we try gives weird results that we're not expecting.

We got it! The answer was staring us right in the face the whole time. This does the trick:
SELECT
COUNT (table1.ID),
table2.DATE
FROM
table1,
(
SELECT
table1.DATEIN AS DATE
FROM
table1
GROUP BY
table1.DATEIN
UNION
SELECT
table1.DATEOUT AS DATE
FROM
table1
WHERE
table1.DATEOUT NOT IN (
SELECT
table1.DATEIN
FROM
table1
GROUP BY
table1.DATEIN
)
GROUP BY
table1.DATEOUT
) table2
WHERE
table1.DATEIN<=table2.DATE
AND
(
table1.DATEOUT>table2.DATE
OR
table1.DATEOUT IS NULL
)
GROUP BY
table2.DATE

Related

Finding Max(Date) BEFORE specified date in Redshift SQL

I have a table (Table A) in SQL (AWS Redshift) where I've isolated my beginning population that contains account id's and dates. I'd like to take the output from that table and LEFT join back to the "accounts" table to ONLY return the start date that precedes or comes directly before the date stored in the table from my output.
Table A (Beg Pop)
-------
select account_id,
min(start_date),
min(end_date)
from accounts
group by 1;
I want to return ONLY the date that precedes the date in my current table where account_id match. I'm looking for something like...
Table B
-------
select a.account_id,
a.start_date,
a.end_date,
b.start_date_prev,
b.end_date_prev
from accounts as a
left join accounts as b on a.account_id = b.account_id
where max(b.start_date) less than a.start_date;
Ultimately, I want to return everything from table a and only the dates where max(start_date) is less than the start_date from table A. I know aggregation is not allowed in the WHERE clause and I guess I can do a subquery but I only want the Max date BEFORE the dates in my output. Any suggestions are greatly appreciated.
I want to return ONLY the date that precedes the date in my current table where account_id match
If you want the previous date for a given row, use lag():
select a.*,
lag(start_date) over (partition by account_id order by start_date) as prev_start_date
from accounts a;
As I understand from the requirement is to display all rows from a base table with the preceeding data sorted based on a column and with some conditions
Please check following example which I took from article Select Next and Previous Rows with Current Row using SQL CTE Expression
WITH CTE as (
SELECT
ROW_NUMBER() OVER (PARTITION BY account_id ORDER BY start_date) as RN,
*
FROM accounts
)
SELECT
PreviousRow.*,
CurrentRow.*,
NextRow.*
FROM CTE as CurrentRow
LEFT JOIN CTE as PreviousRow ON
PreviousRow.RN = CurrentRow.RN - 1 and PreviousRow.account_id = CurrentRow.account_id
LEFT JOIN CTE as NextRow ON
NextRow.RN = CurrentRow.RN + 1 and NextRow.account_id = CurrentRow.account_id
ORDER BY CurrentRow.account_id, CurrentRow.start_date;
I tested with following sample data and it seems to be working
create table accounts(account_id int, start_date date, end_date date);
insert into accounts values (1,'20201001','20201003');
insert into accounts values (1,'20201002','20201005');
insert into accounts values (1,'20201007','20201008');
insert into accounts values (1,'20201011','20201013');
insert into accounts values (2,'20201001','20201002');
insert into accounts values (2,'20201015','20201016');
Output is as follows

Calculate the sum of workdays until today using SQL

I have the Base Table and I am trying to get the Result Table.
I am using this code to get the two first columns of the Result Table.
The Overdue column should give the SUM of the WorkingDays until today's date.
How can I get all 3 columns in the same select statement?
;With cte as (
Select [Id] ,[Event_Id] ,[StartDate] ,[EndDate] ,[WorkingDay] From BaseTable
)
Select
Id,
SUM(WorkingDays) as Planned
From cte
Group By Id, Event_Id

SQL Server : UNION ALL but remove duplicate IDs by choosing first date of occurrence

I am unioning two queries but I'm getting an ID that occurs in each query. I do not know how to keep only the first time the id occurs. Everything else about the row is different. In general, it will be hard to know which of the two queries I will have to keep a duplicate on, therefore, I need a general solution.
I was thinking about creating a temp table and choosing the min date (once the date has been converted to an int).
Any ideas on the proper syntax?
You can do this using the row_number() function. This will assign a sequential number, starting with 1, to each row with the same id (based on the partition by clause). The ordering of the sequence is determined by the order by clause. So, the following assigns 1 to the earliest date for each id:
select t.*
from (select t.*,
row_number() over (partition by id order by date asc) as seqnum
from ((select *
from <subquery1>
) union all
(select *
from <subquery2>
)
) t
) t
where seqnum = 1;
The final where clause simply filters for the first occurrence.
If you use the keyword UNION, then it will remove duplicates from the two data sets you are working with. UNION ALL preserves duplicates.
You can view the specifics here:
http://www.w3schools.com/sql/sql_union.asp
If you want to only have one of the 2 records and they are not identical you will have to filter them yourself. You may need to do something like the following. THis may be possible to do with the one (select union select) block but this should get you started.
select *
from (
select id
, date
, otherstuf
from table_1
union all
select id
, date
, otherstuf
from table_2
) x1
, (
select id
, date
, otherstuf
from table_1
union all
select id
, date
, otherstuf
from table_2
) x2
where x1.id = x2.id
and x1.date < x2.date
Although rethinking this if you go down a path like this why bother to UNION it?

How to select multiple rows in SQL Server while filling one column with the first value

Each of my rows have a date. I want the database to keep the good date. But I am in a situation where I want only the first date. But I still want all the other rows. So I would like to fill the date column with all the same date in my result.
For an example (Because I don't think I expressed myself well)
I have this:
name value date
a 10 5/13
b 14 2/13
c 20 1/13
a 11 7/13
a 5 8/13
b 8 9/13
I want it to become like this in the result:
name value date
a 26 5/13
b 22 5/13
c 20 5/13
I searched for this information but I only find the way to select the first row.
for now I'm doing
SELECT name, SUM(value), date FROM table
ORDER BY name
And I'm kind of clueless for what to do next.
Thanks :)
Databases don't have a concept of "first". Here is an attempt, but no guarantees unless you have a way of ordering to determine first:
select name, sum(value), const.date
from table cross join
(select top 1 date from table) const
group by name, const.date
If you only want to do this for a query, to provide this aggregated data for some specific client requirement, then #freshPrince's answer is appropriate. But if want to actually modify the data in the table itself, and prevent the issue from arising again, then you need to change the schema.
Create Table newTable(
name varChar(30) not null,
date datetime not null,
value decimal(10,2) not null default(0),
primary key (name, date) )
Insert newTable (name, date, value)
Select name, SUM(value), Min(date)
FROM currentTable
Group By Name
and delete the old table... then rename the new table to whatever...
You will also have to modify the process used to insert new rows so that instread of always inserting a new row, it updates the existing row for a specified name and date if it already exists...
Your question is slightly confusing since your desired result is showing a date that does not exists with either b or c but if that is the result that you want want you could use something similar to the following:
select name, sum(value) value, d.date
from yt
cross join
(
select min(date) date
from yt
where name = (select min(name)
from yt)
) d
group by name, d.date;
See SQL Fiddle with Demo
But it seems like you actually would want the min(date) for each name:
select name, sum(value) value, min(date)
from yt
group by name;
See SQL Fiddle with Demo.
If the order of the date should be the determined by the name then you could use:
select t.name, sum(value) value, d.date
from yt t
cross join
(
select top 1 name, date
from yt
order by name, date
) d
group by t.name, d.date;
See Demo

SQL How to remove duplicates within select query?

I have a table which looks like that:
As You see, there are some date duplicates, so how to select only one row for each date in that table?
the column 'id_from_other_table' is from INNER JOIN with the table above
There are multiple rows with the same date, but the time is different. Therefore, DISTINCT start_date will not work. What you need is: cast the start_date to a DATE (so the TIME part is gone), and then do a DISTINCT:
SELECT DISTINCT CAST(start_date AS DATE) FROM table;
Depending on what database you use, the type name for DATE is different.
Do you need any other information except the date? If not:
SELECT DISTINCT start_date FROM table;
You mention that there are date duplicates, but it appears they're quite unique down to the precision of seconds.
Can you clarify what precision of date you start considering dates duplicate - day, hour, minute?
In any case, you'll probably want to floor your datetime field. You didn't indicate which field is preferred when removing duplicates, so this query will prefer the last name in alphabetical order.
SELECT MAX(owner_name),
--floored to the second
dateadd(second,datediff(second,'2000-01-01',start_date),'2000-01-01') AS StartDate
From MyTable
GROUP BY dateadd(second,datediff(second,'2000-01-01',start_date),'2000-01-01')
Select Distinct CAST(FLOOR( CAST(start_date AS FLOAT ) )AS DATETIME) from Table
If you want to select any random single row for particular day, then
SELECT * FROM table_name GROUP BY DAY(start_date)
If you want to select single entry for each user per day, then
SELECT * FROM table_name GROUP BY DAY(start_date),owner_name
here is the solution for your query returning only one row for each date in that table
here in the solution 'tony' will occur twice as two different start dates are there for it
SELECT * FROM
(
SELECT T1.*, ROW_NUMBER() OVER(PARTITION BY TRUNC(START_DATE),OWNER_NAME ORDER BY 1,2 DESC ) RNM
FROM TABLE T1
)
WHERE RNM=1
You have to convert the "DateTime" to a "Date". Then you can easier select just one for the given date no matter the time for that date.