Create row_num column in Access SQL, order by field first - sql

I am trying to write a simple query that will take a table, order by the date field of that table and add a column that includes a row count. This is the easiest thing in T-SQL, but Access does not support the Row_Num() function.
So, let's say my "Dates" table looks like this:
ID Date
1 02/01/2017
2 02/03/2017
3 01/27/2017
4 02/05/2017
5 02/01/2017
6 02/03/2017
And the result of my Access query should look like this:
ID Date RowNum
3 01/27/2017 1
1 02/01/2017 2
5 02/01/2017 3
2 02/03/2017 4
6 02/03/2017 5
4 02/05/2017 6
I have tried to find an answer to this question, but all the answers I have found seem to rely on the difference in the values of the ID field from one row to the next. So then I tried to apply the concepts I found (creating a column with a dcount where A.ID > ID) to the Date field, but then I get a count per date. But I need a count for every single date, even if there might be multiple dates that are the same.
Thanks in advance

One method is a correlated subquery:
select d.*,
(select count(*) from dates as d2 where d2.date <= d.date) as rownum
from dates as d
order by d.date;
This is not very efficient, but on a small table it does accomplish what you want. The simplest way, though, is probably to use a cursor over the table.
This assumes that the dates are distinct, as in the example data in the question.
EDIT:
On closer inspection, the dates are not unique. So you can use multiple conditions:
select d.*,
(select count(*)
from dates as d2
where d2.date < d.date or
(d2.date = d.date and d2.id <= d.id)
) as rownum
from dates as d
order by d.date;

Related

Extra column looking at where OpenDate > ClosedDate prevoius records

I have a hard struggle with this problem.
I have the following table:
TicketNumber
OpenTicketDate YYYY_MM
ClosedTicketDate YYYY_MM
1
2018-1
2020-1
2
2018-2
2021-2
3
2019-1
2020-6
4
2020-7
2021-1
I would like to create an extra column which would monitor the open tickets at the given OpenTicketDate.
So the new table would look like this:
TicketNumber
OpenTicketDate YYYY_MM
ClosedTicketDate YYYY_MM
OpenTicketsLookingBackwards
1
2018-1
2020-1
1
2
2018-2
2021-2
2
3
2019-1
2020-6
3
4
2020-7
2021-1
2
The logic behind the 4th (extra) column is that it looks at the previous records & current record where the ClosedTicketsDate > OpenTicketDate.
For example ticketNumber 4 has '2' open tickets because there are only 2 ClosedTicketDate records where ClosedTicketDate > OpenTicketDate.
The new column only fills data based on looking at prevoius records. It is backward looking not forward.
Is there anyone who can help me out?
You could perform a self join and aggregate as the following:
Select T.TicketNumber, T.OpenTicketDate, T.ClosedTicketDate,
Count(*) as OpenTicketsLookingBackwards
From table_name T Left Join table_name D
On Cast(concat(T.OpenTicketDate,'-1') as Date) < Cast(concat(D.ClosedTicketDate,'-1') as Date)
And T.ticketnumber >= D.ticketnumber
Group By T.TicketNumber, T.OpenTicketDate, T.ClosedTicketDate
Order By T.TicketNumber
You may also try with a scalar subquery as the following:
Select T.TicketNumber, T.OpenTicketDate, T.ClosedTicketDate,
(
Select Count(*) From table_name D
Where Cast(concat(T.OpenTicketDate,'-1') as Date) <
Cast(concat(D.ClosedTicketDate,'-1') as Date)
And T.ticketnumber >= D.ticketnumber
) As OpenTicketsLookingBackwards
From table_name T
Order By T.TicketNumber
Mostly, joins tend to outperform subqueries.
See a demo.

Is there a way to display the first two results of each unique id?

I work in healthcare. In a Postgres database, we have a table member IDs and dates. I'm trying to pull the latest two dates for each member ID.
Simplified sample data:
A 1
B 1
B 2
C 1
C 5
C 7
D 1
D 2
D 3
D 4
Desired result:
A 1
B 1
B 2
C 1
C 5
D 1
D 2
I get a strong feeling this is for a homework assignment and would recommend that you look into partitioning and specifically rank() function by yourself first before looking at my solution.
Moreover, you have not specified how you received the initial result you provided, so I'll have to assume you just did select letter_column, number_column from my_table; to achieve the result.
So, what you actually want here is partition the initial query result into groups by the letter_column and select the first two rows in each. rank() function lets you assign each row a number, counting within groups:
select letter_column,
number_column,
rank() over (partition by letter_column order by number_column) as rank
from my_table;
Since it's a function, you can't use it in a predicate in the same query, so you'll have to build another query around this one, this time filtering the results where rank is over 2:
with ranked_results as (select letter_column,
number_column,
rank() over (partition by letter_column order by number_column asc) as rank
from my_table mt)
select letter_column,
number_column
from ranked_results
where rank < 3;
Here's an SQLFiddle to play around: http://sqlfiddle.com/#!15/e90744/1/0
Hope this helps!

Fill missing dates in PostgreSQL with zero

I've a query like this in PostgreSQL:
select count(id_student) students, date_beginning_course from
data.sessions_courses
left join my_schema.students on id_session_course=id_sesion
where course_name='First course'
group by date_beginning_course
What I obtain with this query is the number of students that have attended a session of "First course" in several dates, for example:
Students Date_beginning_course
____________________________________
5 2019-06-26
1 2019-06-28
5 2019-06-30
6 2019-07-01
2 2019-07-02
I'd like to fill this table with the missing date values, and, for each missing value, assign a '0' in Students column, because there are no students for this date. Example:
Students Date_beginning_course
____________________________________
5 2019-06-26
0 2019-06-27 <--new row
1 2019-06-28
0 2019-06-29 <--new row
5 2019-06-30
6 2019-07-01
2 2019-07-02
Could you help me? Thanks! :)
You could generate a list of dates with the handy Postgres set-returning function generate_series() and LEFT JOIN it with the sessions_courses and students table:
SELECT
COUNT(s.id_student) students,
d.dt
FROM
(
SELECT dt::date
FROM generate_series('2019-06-26', '2019-07-02', '1 day'::interval) dt
) d
LEFT JOIN data.sessions_courses c
ON c.date_beginning_course = d.dt
AND c.course_name='First course'
LEFT JOIN my_schema.students s
ON s.id_session_course = c.id_session
GROUP BY d.dt
You can change the date range by modifying the first two parameters of generate_series().
NB: it is a general good practive to index the column names in the query with the relevant table names (or table alias), so it is explicit to which table each column belongs. I changed your query accordingly, and had to make a few assumptions, that you might need to adapt.

Adding in missing dates from results in SQL

I have a database that currently looks like this
Date | valid_entry | profile
1/6/2015 1 | 1
3/6/2015 2 | 1
3/6/2015 2 | 2
5/6/2015 4 | 4
I am trying to grab the dates but i need to make a query to display also for dates that does not exist in the list, such as 2/6/2015.
This is a sample of what i need it to be:
Date | valid_entry
1/6/2015 1
2/6/2015 0
3/6/2015 2
3/6/2015 2
4/6/2015 0
5/6/2015 4
My query:
select date, count(valid_entry)
from database
where profile = 1
group by 1;
This query will only display the dates that exist in there. Is there a way in query that I can populate the results with dates that does not exist in there?
You can generate a list of all dates that are between the start and end date from your source table using generate_series(). These dates can then be used in an outer join to sum the values for all dates.
with all_dates (date) as (
select dt::date
from generate_series( (select min(date) from some_table), (select max(date) from some_table), interval '1' day) as x(dt)
)
select ad.date, sum(coalesce(st.valid_entry,0))
from all_dates ad
left join some_table st on ad.date = st.date
group by ad.date, st.profile
order by ad.date;
some_table is your table with the sample data you have provided.
Based on your sample output, you also seem to want group by date and profile, otherwise there can't be two rows with 2015-06-03. You also don't seem to want where profile = 1 because that as well wouldn't generate two rows with 2015-06-03 as shown in your sample output.
SQLFiddle example: http://sqlfiddle.com/#!15/b0b2a/2
Unrelated, but: I hope that the column names are only made up. date is a horrible name for a column. For one because it is also a keyword, but more importantly it does not document what this date is for. A start date? An end date? A due date? A modification date?
You have to use a calendar table for this purpose. In this case you can create an in-line table with the tables required, then LEFT JOIN your table to it:
select "date", count(valid_entry)
from (
SELECT '2015-06-01' AS d UNION ALL '2015-06-02' UNION ALL '2015-06-03' UNION ALL
'2015-06-04' UNION ALL '2015-06-05' UNION ALL '2015-06-06') AS t
left join database AS db on t.d = db."date" and db.profile = 1
group by t.d;
Note: Predicate profile = 1 should be applied in the ON clause of the LEFT JOIN operation. If it is placed in the WHERE clause instead then LEFT JOIN essentially becomes an INNER JOIN.

SQL select and count all items that have occured before

I have a table with rows that symbolize order dates:
2009-05-15 13:31:47.713
2009-05-15 22:09:32.227
2009-05-16 02:38:36.027
2009-05-16 12:06:49.743
2009-05-16 16:20:26.680
2009-05-17 01:36:19.480
2009-05-18 09:44:46.993
2009-05-18 14:06:12.073
2009-05-18 15:25:47.540
2009-05-19 10:28:24.150
I would like have query that returns the following:
2009-05-15 2
2009-05-16 5
2009-05-17 6
2009-05-18 9
2009-05-19 10
Basically it keeps a running total of all the orders placed by the end of the day of the date indicated. The orders are not the orders on that day but all the orders since the earliest dates in the table.
This is MSSQL 2000 and the datatype in the first table is just datetime, in the second it could be datetime or string, it doesn't really matter for my purposes.
I got this to work on SQL Server 2005. I think it should work with 2000, as well.
SELECT dt, count(q2.YourDate)
FROM (SELECT DISTINCT CONVERT(varchar,YourDate,101) dt FROM YourTable) t1
JOIN YourTable q2 ON DATEADD(d,-1,CONVERT(varchar,YourDate,101)) < dt
GROUP BY dt
This will query the table twice, but at least gives correct output.
I recommend a 2 query solution. This is slow, but I use this method almost daily. The important thing is to NOT join the 2 tables in the first query. You want the duplication of each order for every date in your lookup table.
You will need a Lookup table with 1 row for each date of the time period you're interested in. Let's call it dboDateLookup. Here's what it will look like:
DtIndex
2009-05-15
2009-05-16
2009-05-17
2009-05-18
2009-05-19
Let's also assume the order table, dboOrders has 2 columns, ordernumber and orderdate.
ordernumber orderdate
2009-05-15 13:31:47.713 1
2009-05-15 22:09:32.227 2
2009-05-16 02:38:36.027 3
2009-05-16 12:06:49.743 4
2009-05-16 16:20:26.680 5
Query1:
SELECT
Format([ordernumber],"yyyy-mm-dd") AS ByDate,
ordernumber,
(If Format([orderdate],"yyyy-mm-dd")<=[DtIndex],1,0) AS NumOrdersBefore
FROM [dboOrders], [dboDateLookUp];
Query2:
Select
[ByDate],
sum([NumOrdersBefore]) as RunningTotal
from [Query1];
Try this (returns string dates):
SELECT
LEFT(CONVERT(char(23),YourDate,121),10) AS Date
,COUNT(*) AS CountOf
FROM YourTable
GROUP BY LEFT(CONVERT(char(23),YourDate,121),10)
ORDER BY 1
this will table scan. if it is too slow, consider using a persistant computed column with an index for the date, that will run much faster. However, I'mnot sure if you can do all that in SQL 2000.
EDIT read the question better, try this:
SELECT
d.YourDate
,SUM(dt.CountOf) AS CountOf
FROM (SELECT
LEFT(CONVERT(char(23),YourDate,121),10) AS Date
,COUNT(*) AS CountOf
FROM YourTable
GROUP BY LEFT(CONVERT(char(23),YourDate,121),10)
) dt
INNER JOIN (SELECT
DISTINCT LEFT(CONVERT(char(23),YourDate,121),10) AS Date
FROM YourTable
) d ON dt.Date<=LEFT(CONVERT(char(23),d.YourDate,121),10)
GROUP BY d.YourDate
ORDER BY d.YourDate
I have another one
It is not so fancy
I ran it on Access so syntax may differ little bit.
But it seems to work.
P.S. Im relatively new to SQL
Data:
ID F1 F2
1 15/05/2009 13:31:47.713
2 15/05/2009 22:09:32.227
3 16/05/2009 02:38:36.027
4 16/05/2009 12:06:49.743
5 16/05/2009 16:20:26.680
6 17/05/2009 01:36:19.480
7 18/05/2009 09:44:46.993
8 18/05/2009 14:06:12.073
9 18/05/2009 15:25:47.540
10 19/05/2009 10:28:24.150
Query:
SELECT Table1.F1 AS Dates, SUM(REPLACE(Len(Table1.F2), Len(Table1.F2), 1)) AS Occurred
FROM Table1
GROUP BY Table1.F1;
Result:
Dates Occurred
15/05/2009 2
16/05/2009 3
17/05/2009 1
18/05/2009 3
19/05/2009 1
SELECT Count(*), LEFT(CONVERT(char(23),YourDate,121),10) AS Date FROM
(SELECT
DISTINCT LEFT(CONVERT(char(23),YourDate,121),10) AS Date
FROM YourTable
GROUP BY LEFT(CONVERT(char(23),YourDate,121),10)) x //Gets the distinct dates.
INNER JOIN YourTable y on x.Date >= y.Date
GROUP BY LEFT(CONVERT(char(23),YourDate,121),10)
It's going to be slow. REALLY REALLY slow. I hate to think what run times would be.