How to get all days in one table a date range even if no data exists also in SQL Server - mysqli

I have one table name called Tab1. I would like to get all date even if any one of the days is missing also.
+-------------------+--------------------------+
|Name | dateCheck |
+-------------------+--------------------------+
| 1 | 2016-01-01 00:00:00.000 |
| 2 | 2016-01-02 00:00:00.000 |
| 3 | 2016-01-05 00:00:00.000 |
| 4 | 2016-01-07 00:00:00.000 |
+-------------------+--------------------------+
I need output like below :
+-------------------+--------------------------+
|Name | dateCheck |
+-------------------+--------------------------+
| 1 | 2016-01-01 00:00:00.000 |
| 2 | 2016-01-02 00:00:00.000 |
| 0 | 2016-01-03 00:00:00.000 |
| 0 | 2016-01-04 00:00:00.000 |
| 3 | 2016-01-05 00:00:00.000 |
| 0 | 2016-01-06 00:00:00.000 |
| 4 | 2016-01-07 00:00:00.000 |

You may use a calendar table:
SELECT
COALESCE(t2.Name, 0) AS Name,
t1.dateCheck
FROM
(
SELECT '2016-01-01' AS dateCheck UNION ALL
SELECT '2016-01-02' UNION ALL
SELECT '2016-01-03' UNION ALL
SELECT '2016-01-04' UNION ALL
SELECT '2016-01-05' UNION ALL
SELECT '2016-01-06' UNION ALL
SELECT '2016-01-07'
) t1
LEFT JOIN yourTable t2
ON t1.dateCheck = t2.dateCheck;

Related

How to list rows with duplicate columns

I have a table with the fields id, name, birthday, clinic:
id | name | birthday | clinic
1 | mary | 2020-01-01 | clin 1
2 | mary | 2020-01-01 | clin 1
3 | mary | 2020-01-01 | clin 2
4 | john | 2021-01-01 | clin 1
5 | pete | 2020-01-05 | clin 1
6 | pete | 2020-01-05 | clin 2
7 | pete | 2020-01-05 | clin 3
I want to get all records with name, birthday duplicate like:
id | name | birthday | clinic
1 | mary | 2020-01-01 | clin 1
2 | mary | 2020-01-01 | clin 1
3 | mary | 2020-01-01 | clin 2
5 | pete | 2020-01-05 | clin 1
6 | pete | 2020-01-05 | clin 2
7 | pete | 2020-01-05 | clin 3
Mary and Pete have more than one record with same name and birthday
Using COUNT() as an analytical function, we can try:
WITH cte AS (
SELECT *, COUNT(*) OVER (PARTITION BY name, birthday) cnt
FROM yourTable
)
SELECT id, name, birthday, clinic
FROM cte
WHERE cnt > 1;
try
select * from <table> where (name , birthday) in (
select name , birthday from <table> group by name, birthday having count(*)>1)
You can use an EXISTS condition:
select t1.*
from the_table t1
where exists (select *
from the_table t2
where t1.id <> t2.id
and (t1.name, t1.birthday) = (t2.name, t2.birthday));

PostgreSQL build working range from one date column

I'm using PostgreSQL v. 11.2
I have a table
|id | bot_id | date |
| 1 | 1 | 2020-04-20 16:00:00|
| 2 | 2 | 2020-04-22 12:00:00|
| 3 | 3 | 2020-04-24 04:00:00|
| 4 | 1 | 2020-04-27 09:00:00|
And for example, I have DateTime range 2020-03-30 00:00:00 and 2020-04-30 00:00:00
I need to show get working ranges to count the total working hours of each bot.
Like this:
|bot_id | start_date | end_date |
| 1 | 2020-03-30 00:00:00 | 2020-04-20 16:00:00 |
| 2 | 2020-04-20 16:00:00 | 2020-04-22 12:00:00 |
| 3 | 2020-04-22 12:00:00 | 2020-04-24 04:00:00 |
| 1 | 2020-04-24 04:00:00 | 2020-04-27 09:00:00 |
| 1 | 2020-04-27 09:00:00 | 2020-04-30 00:00:00 |
I've tried to use LAG(date) but I'm not getting first and last dates of the range.
You could use a UNION ALL, with one part building the start_date/end_date couples from your values & the other part filling in the last period (from the last date to 2020-04-30 00:00:00):
WITH values (id, bot_id, date) AS (
VALUES (1, 1, '2020-04-20 16:00:00'::TIMESTAMP)
, (2, 2, '2020-04-22 12:00:00')
, (3, 3, '2020-04-24 04:00:00')
, (4, 1, '2020-04-27 09:00:00')
)
(
SELECT bot_id
, LAG(date, 1, '2020-03-30 00:00:00') OVER (ORDER BY id) AS start_date
, date AS end_date
FROM values
)
UNION ALL
(
SELECT bot_id
, date AS start_date
, '2020-04-30 00:00:00' AS end_date
FROM values
ORDER BY id DESC
LIMIT 1
)
+------+--------------------------+--------------------------+
|bot_id|start_date |end_date |
+------+--------------------------+--------------------------+
|1 |2020-03-30 00:00:00.000000|2020-04-20 16:00:00.000000|
|2 |2020-04-20 16:00:00.000000|2020-04-22 12:00:00.000000|
|3 |2020-04-22 12:00:00.000000|2020-04-24 04:00:00.000000|
|1 |2020-04-24 04:00:00.000000|2020-04-27 09:00:00.000000|
|1 |2020-04-27 09:00:00.000000|2020-04-30 00:00:00.000000|
+------+--------------------------+--------------------------+

Postgresql - increment counter in rows where a column has duplicate value

I have added a column (seq) to a table used for scheduling so the front end can manage the order in which each item can be displayed. Is it possible to craft a SQL query to populate this column with an incremental counter based on the common duplicate values in the date column?
Before
------------------------------------
| name | date_time | seq |
------------------------------------
| ABC1 | 15-01-2017 11:00:00 | |
| ABC2 | 16-01-2017 11:30:00 | |
| ABC1 | 16-01-2017 11:30:00 | |
| ABC3 | 17-01-2017 10:00:00 | |
| ABC3 | 18-01-2017 12:30:00 | |
| ABC4 | 18-01-2017 12:30:00 | |
| ABC1 | 18-01-2017 12:30:00 | |
------------------------------------
After
------------------------------------
| name | date_time | seq |
------------------------------------
| ABC1 | 15-01-2017 11:00:00 | 0 |
| ABC2 | 16-01-2017 11:30:00 | 0 |
| ABC1 | 16-01-2017 11:30:00 | 1 |
| ABC3 | 17-01-2017 10:00:00 | 0 |
| ABC3 | 18-01-2017 12:30:00 | 0 |
| ABC4 | 18-01-2017 12:30:00 | 1 |
| ABC1 | 18-01-2017 12:30:00 | 2 |
------------------------------------
Solved, thanks to both answers.
To make it easier for anybody who finds this, the working code is:
UPDATE my_table f
SET seq = seq2
FROM (
SELECT ctid, ROW_NUMBER() OVER (PARTITION BY date_time ORDER BY ctid) -1 AS seq2
FROM my_table
) s
WHERE f.ctid = s.ctid;
Use the window function row_number():
with my_table (name, date_time) as (
values
('ABC1', '15-01-2017 11:00:00'),
('ABC2', '16-01-2017 11:30:00'),
('ABC1', '16-01-2017 11:30:00'),
('ABC3', '17-01-2017 10:00:00'),
('ABC3', '18-01-2017 12:30:00'),
('ABC4', '18-01-2017 12:30:00'),
('ABC1', '18-01-2017 12:30:00')
)
select *,
row_number() over (partition by name order by date_time)- 1 as seq
from my_table
order by date_time;
name | date_time | seq
------+---------------------+-----
ABC1 | 15-01-2017 11:00:00 | 0
ABC1 | 16-01-2017 11:30:00 | 1
ABC2 | 16-01-2017 11:30:00 | 0
ABC3 | 17-01-2017 10:00:00 | 0
ABC1 | 18-01-2017 12:30:00 | 2
ABC3 | 18-01-2017 12:30:00 | 1
ABC4 | 18-01-2017 12:30:00 | 0
(7 rows)
Read this answer for a similar question about updating existing records with a unique integer.
Check out ROW_NUMBER().
SELECT name, date_time, ROW_NUMBER() OVER (PARTITION BY date_time ORDER BY name) FROM [table]

Use another table's data in postgreSQL

I have a event table and a transaction log.
And I want count each event's total revenue by one sql.
Could anything tell how to do this.
please be ware there will be more than 100,000 logs in transaction table.
event_table:
Event_id | start_date | end_date
------------------------
11111 | 2013-01-04 | 2013-01-05
11112 | 2013-01-08 | 2013-01-10
11113 | 2013-01-11 | 2013-01-12
11114 | 2013-01-15 | 2013-01-18
11115 | 2013-01-19 | 2013-01-21
11116 | 2013-01-22 | 2013-01-24
11117 | 2013-01-26 | 2013-01-29
transaction_log:
id | name | time_created | Cost
------------------------
1 | michael | 2013-01-04 | 1
2 | michael | 2013-01-08 | 4
3 | mary | 2013-01-11 | 5
4 | john | 2013-01-15 | 2
5 | michael | 2013-01-19 | 3
6 | mary | 2013-01-22 | 2
7 | john | 2013-01-26 | 4
I tried to use the sql like following, but it does not work.
select
event_table.id,
( select sum(Cost)
from transaction_log
where date(time_created) between transaction_log.start_date and transaction_log.end_date ) as revenue
from event_table
It is failing because the fields start_date and end_date are from event_table but you're stating them as transaction_log.start_date and transaction_log.end_date. This will work:
select
event_table.id,
( select sum(Cost)
from transaction_log
where date(time_created) between event_table.start_date and event_table.end_date ) as revenue
from event_table
There is no need to cast time_created as date (date(time_created)) if it is already of date data type. Otherwise, if time_created is timestamp or timestamptz, then for performance you may want to consider doing:
select
event_table.id,
( select sum(Cost)
from transaction_log
where time_created >= event_table.start_date::timestamptz and time_created < (event_table.end_date+1)::timestamptz ) as revenue
from event_table
Also for performance, when executing a query like the one above, PostgreSQL is executing a subquery for each row of the main query (in this case the event_table table). Joining and using GROUP BY will generally provide you with better results:
select e.id, sum(l.Cost) as revenue
from event_table e
join transaction_log l ON (l.time_created BETWEEN e.start_date AND e.end_date)
group by e.id

Aggregate count by several weeks after field data in PostgreSQL

I have a query returns something like that:
registered_at - date of user registration;
action_at - date of some kind of action.
| registered_at | user_id | action_at |
-------------------------------------------------------
| 2015-05-01 12:00:00 | 1 | 2015-05-04 12:00:00 |
| 2015-05-01 12:00:00 | 1 | 2015-05-10 12:00:00 |
| 2015-05-01 12:00:00 | 1 | 2015-05-16 12:00:00 |
| 2015-04-01 12:00:00 | 2 | 2015-04-04 12:00:00 |
| 2015-04-01 12:00:00 | 2 | 2015-04-05 12:00:00 |
| 2015-04-01 12:00:00 | 2 | 2015-04-10 12:00:00 |
| 2015-04-01 12:00:00 | 2 | 2015-04-30 12:00:00 |
I'm trying to implement query that will returns me something like that:
weeks_after_registration - in this example limited by 3, in real task it will be limited by 6.
| user_id | weeks_after_registration | action_counts |
-------------------------------------------------------
| 1 | 1 | 1 |
| 1 | 2 | 1 |
| 1 | 3 | 1 |
| 2 | 1 | 2 |
| 2 | 2 | 1 |
| 2 | 3 | 0 |
You can use extract(days from (action_at - registered_at) / 7)+1 to get the number of weeks. Then count the number of actions grouped by the number of weeks.
select user_id, wk, count(*) actions
from (select user_id, extract(days from (action_at - registered_at) / 7)+1 wk from Table1) a
where wk <= 3
group by user_id, wk
If you must display rows where action_counts = 0 in the result, then you need to join with the all possible week numbers (1, 2, 3) and all possible user_ids (1, 2) like:
select b.user_id, a.wk, coalesce(c.actions, 0) actions
from (select * from generate_series(1, 3) wk) a
join (select distinct user_id from Table1) b on true
left join (
select user_id, wk, count(*) actions
from (select user_id, extract(days from (action_at - registered_at) / 7)+1 wk from Table1) a
where wk <= 3
group by user_id, wk
) c on a.wk = c.wk and b.user_id = c.user_id
order by b.user_id, a.wk;
fiddle