How merge two tables and average it (hourly vs daily tables) - sql

I have the following tables:
CREATE TABLE a (DATE TEXT, PRICE INTEGER)
INSERT INTO a VALUES
("2019-04-27", 10), ("2019-04-29",20), ("2019-04-30",30), ("2019-05-01",40);
CREATE TABLE b (DATE TEXT, PRICE INTEGER)
INSERT INTO b VALUES
("2019-04-27 01:00", 1), ("2019-04-27 02:30)",3), ("2019-04-27 18:00",2),
("2019-04-28 17:00",2), ("2019-04-28 21:00",5),
("2019-04-29 17:00",50), ("2019-04-29 21:00",10),
("2019-04-30 17:00",10), ("2019-04-30 21:00",20),
("2019-05-01 17:00",40), ("2019-05-01 21:00",10),
("2019-05-02 17:00",10), ("2019-05-02 21:00",6);
I need to merge this two tables, so that Table b is averaged to daily and table has 2 columns (1 is date (all dates are necessary to be there) and 2 is Price (Null if no observations for that date). I tried several left joins , however do not know how to tackle the problem that I cannot average hourly data to the daily.
Could you help?

Please, execute query as per below SQL-Fiddle:
select DATE(c.date) as date, avg(c.price) as avg_price
from
(select date, price
from a
union all
select date, price
from b
) as c
group by DATE(c.date);

I suspect that you want a result set with two columns. I'm not a fan of having the date be in a string datatype, but you can use string functions for what you want:
select date, sum(price_a) as price_a, sum(price_b) as price_b
from (select a.date, a.price as price_a, null as price_b
from a
union all
select substr(b.date, 1, 10), null, price
from b
) ab
group by date;

Related

sql subtraction on two different tables

I have two different tables, common column is truck_id.
I need to subtract two tables from each other to find the net amount.
The result I want:
truck_id
difference
35kd85
1500
35hh52
900
SELECT
(SELECT SUM(last_revenue) FROM (
SELECT DISTINCT last_revenue FROM
Expedition WHERE YEAR(departure_date) > 2020 AND truck_id = '31adc444'
UNION ALL SELECT last_revenue FROM
ChainingExpedition WHERE YEAR(departure_date) > 2020 AND truck_id = '31adc444'
)x
)-(SELECT SUM(price_dollar) FROM (
SELECT DISTINCT price_dollar FROM TruckMaintenanceExpense
WHERE YEAR(payment_date) > 2020 AND expense_type = 'çeker'
AND truck_id ='31adc444'
)x
) AS difference
SQL subtraction on two different tables
When I type truck_id in my query, I get the right result, but my goal is to draw as a list.
Typically, I would create a list of all trucks that meet the criteria you are looking for. Then, I'll get a list of all trucks with revenue and a separate list of trucks with expense. Then you join those 3 tables together and do the math. You query was very hard to follow without good indenting and structure. Next time you ask a question, be sure to include sample data. You should be writing all the CREATE TABLE and INSERT INTO statements that I include in the EXAMPLE DATA section in the fiddle below.
--*****EXAMPLE DATA*****
CREATE TABLE Expedition (
truck_id nvarchar(50)
, last_revenue decimal(19,2)
, departure_date datetime
);
CREATE TABLE TruckMaintenanceExpense (
truck_id nvarchar(50)
, price_dollar decimal(19,2)
, payment_date datetime
);
INSERT INTO Expedition (truck_id, last_revenue, departure_date)
VALUES ('35kd85', 2000.00, '2020-1-1')
, ('35kd85', 3500.00, '2020-1-1')
, ('35hh52', 300.00, '2020-1-1')
, ('35hh52', 258.98, '2020-1-1')
;
INSERT INTO TruckMaintenanceExpense (truck_id, price_dollar, payment_date)
VALUES ('35kd85', 9865.23, '2020-2-1')
, ('35kd85', 321.54, '2020-2-1')
, ('35hh52', 159.78, '2020-2-1')
, ('35hh52', 598.77, '2020-2-1')
;
--*****END EXAMPLE DATA*****
--Create a list of all truck_ids. It would be more helpful
--if you had a table that defined all the trucks (i.e. dbo.trucks).
WITH AllTrucks as (
SELECT truck_id
FROM TruckMaintenanceExpense
UNION ALL
SELECT truck_id
FROM Expedition
)
SELECT DISTINCT
a.truck_id
--Use ISNULL to make sure we have 0.00 if the truck is missing
--either revenue or expense.
, ISNULL(tRev.total_revenue,0) - ISNULL(tExp.total_expense,0) as difference
--Get the list of truck_ids. Use SELECT DISTINCT to elimiate duplicates.
FROM AllTrucks as a
--Get a list of all trucks with Expedition reveue.
LEFT OUTER JOIN (
SELECT
e.truck_id
, YEAR(e.departure_date) as [year]
, SUM(e.last_revenue) as total_revenue
FROM Expedition as e
WHERE e.departure_date >= '2020-1-1 00:00:00'
AND e.departure_date < '2021-1-1 00:00:00'
GROUP BY e.truck_id, YEAR(departure_date)
) as tRev
ON tRev.truck_id = a.truck_id
--Get a list of all trucks with maintenance expenses.
LEFT OUTER JOIN (
SELECT
tme.truck_id
, YEAR(tme.payment_date) as [year]
, SUM(tme.price_dollar) as total_expense
FROM TruckMaintenanceExpense as tme
WHERE tme.payment_date >= '2020-1-1 00:00:00'
AND tme.payment_date < '2021-1-1 00:00:00'
GROUP BY tme.truck_id, YEAR(payment_date)
) as tExp
ON tExp.truck_id = a.truck_id
;
truck_id
difference
35hh52
-199.57
35kd85
-4686.77
fiddle

Sql Server : Select Date between two dates and Display all results including duplicate

Stuck in basic group by I guess.
select date,* from table
This is returning me some 7000 rows.
Suppose min(date) is '1989-5-12' and max(date) is '2005-5-12', Then
select * from table where date between '1989-5-12' and '2005-5-12'
Its returning me around 6000 rows.
Where are the remaining rows.? How can I get the complete result (7000 rows) with where clause of date between.
Obviously you have NULLs in your data:
select * from table
where date between '1989-5-12' and '2005-5-12' or date is null
Sample table:
('20150101'),
(null),
('20150102'),
('20150103'),
(null)
select * from table --will return 5 rows
select * from table
where date between '20150101' and '20150103' --will return 3 rows
select * from table
where date not between '20150101' and '20150103' --will return 0 rows
Please try below
select * from table where date>='1989-5-12' and date<= '2005-5-12'
The remaining rows do not fit your where statement. To see the remaining rows just do:
select * from table where date NOT between '1989-5-12' and '2005-5-12'
Without any data we can not say why you do not get them from your select statement.

How to select values by date field (not as simple as it sounds)

I have a table called tblMK The table contains a date time field.
What I wish to do is create a query which will each time, select the 2 latest entries (by the datetime column) and then get the date difference between them and show only that.
How would I go around creating this expression. This doesn't necessarily need to be a query, it could be a view/function/procedure or what ever works. I have created a function called getdatediff which receives to dates, and returns a string the says (x days y hours z minutes) basically that will be the calculated field. So how would I go around doing this?
Edit: I need to each time select 2 and 2 and so on until the oldest one. There will always be an even amount of rows.
Use only sql like this:
create table t1(c1 integer, dt datetime);
insert into t1 values
(1, getdate()),
(2, dateadd(day,1,getdate())),
(3, dateadd(day,2,getdate()));
with temp as (select top 2 dt
from t1
order by dt desc)
select datediff(day,min(dt),max(dt)) as diff_of_dates
from temp;
sql fiddle
On MySQL use limit clause
select max(a.updated_at)-min(a.updated_at)
From
( select * from mytable order by updated_at desc limit 2 ) a
Thanks guys I found the solution please ignore the additional columns they are for my db:
; with numbered as (
Select part,taarich,hulia,mesirakabala,
rowno = row_number() OVER (Partition by parit order.by taarich)
From tblMK)
Select a.rowno-1,a.part, a.Julia,b.taarich,as.taarich_kabala,a.taarich, a.mesirakabala,getdatediff(b.taarich,a.taarich) as due
From numbered a
Left join numbered b ON b.parit=a.parit
And b.rowno = a.rowno - 1
Where b.taarich is not null
Order by part,taarich
Sorry about mistakes I might of made, I'm on my smartphone.

Postgres: GROUP BY several column

I have two table in this example.
( example column name )
First is the product
product_id | product_text
Second table is Price.
price_productid | price_datestart | price_price
Let's just say I have multiple datestart with the same product. How can I get the actual price ?
If I use GROUP BY in Postgres, with all the selected column, 2 row may come for the same product. Because the column price_datestart is different.
Example :
product_id : 1
product_text : "Apple Iphone"
price_productid : 1
price_datestart :"2013-10-01"
price_price :"99"
price_productid : 1
price_datestart :"2013-12-01"
price_price :"75"
If I try this :
SELECT price_productid,price_datestart,price_price,product_text,product_id
WHERE price_datestart > now()
GROUP BY price_productid,price_datestart,price_price,product_text,product_id
ORDER BY price_datestart ASC
It will give me a result, but two rows and I need one.
Use distinct on syntax. If you want current price:
select distinct on (p.productid)
p.productid, pr.product_text, p.price, p.datestart
from Price as p
left outer join Product as pr on pr.productid = p.productid
where p.datestart <= now()
order by p.productid, p.datestart desc
sql fiddle demo
You have a few problems, but GROUP BY is not one of them.
First, although you have a datestart you don't have a dateend. I'd change datestart to be a daterange, for example:
CREATE TABLE product
(
product_id int
,product_text text
);
CREATE TABLE price
(
price_productid int
,price_daterange TSRANGE
,price_price NUMERIC(10,2)
);
The TSRANGE allows you to set up validity of your price over a given range, for example:
INSERT INTO product VALUES(1, 'phone');
INSERT INTO price VALUES(1, '[2013-08-01 00:00:00,2013-10-01 00:00:00)', 199);
INSERT INTO price VALUES(1, '[2013-10-01 00:00:00,2013-12-01 00:00:00)', 99);
INSERT INTO price VALUES(1, '[2013-12-01 00:00:00,)', 75);
And that makes your SELECT much more simple, for example:
SELECT price_productid,price_daterange,price_price,product_text,product_id
FROM product, price
WHERE price_daterange #> now()::timestamp
AND product_id = price_productid
This also has the benefit of allowing you to query for any arbitrary time by swapping out now() for another date.
You should read up on ranges in PostgresQL as they are very powerful. The example above is not complete in that it should also have indices on price_daterange to ensure that you do not have overlaps for any product.
SQL fiddle with above solution

SQL JOIN table with a date range

Say, I have a table with C columns and N rows. I would like to produce a select statement that represents the "join" of that table with a data range comprising, M days. The resultant result set should have C+1 columns (the last one being the date) and NXM rows.
Trivial example to clarify things:
Given the table A below:
select * from A;
avalue |
--------+
"a" |
And a date range from 10 to 12 of October 2012, I want the following result set:
avalue | date
--------+-------
"a" | 2012-10-10
"a" | 2012-10-11
"a" | 2012-10-12
(this is a stepping stone I need towards ultimately calculating inventory levels on any given day, given starting values and deltas)
The Postgres way for this is simple: CROSS JOIN to the function generate_series():
SELECT t.*, g.day::date
FROM tbl t
CROSS JOIN generate_series(timestamp '2012-10-10'
, timestamp '2012-10-12'
, interval '1 day') AS g(day);
Produces exactly the output requested.
generate_series() is a set-returning function (a.k.a. "table function") producing a derived table. There are a couple of overloaded variants, here's why I chose timestamp input:
Generating time series between two dates in PostgreSQL
For arbitrary dates, replace generate_series() with a VALUES expression. No need to persist a table:
SELECT *
FROM tbl t
CROSS JOIN (
VALUES
(date '2012-08-13') -- explicit type in 1st row
, ('2012-09-05')
, ('2012-10-10')
) g(day);
If the date table has more dates in it than you're interested in, then do
select a.avalue, b.date from a, b where b.date between '2012-10-10' and '2012-10-12'
Other wise if the date table contained only the dates you were interested in, a cartesian join would accomplish this:
select * from a,b;
declare
#Date1 datetime = '20121010',
#Date2 datetime = '20121012';
with Dates
as
(
select #Date1 as [Date]
union all
select dateadd(dd, 1, D.[Date]) as [Date]
from Dates as D
where D.[Date] <= DATEADD(dd, -1, #Date2)
)
select
A.value, D.[Date]
from Dates as D
cross join A
For MySQL
schema/data:
CREATE TABLE someTable
(
someCol varchar(8) not null
);
INSERT INTO someTable VALUES ('a');
CREATE TABLE calendar
(
calDate datetime not null,
isBus bit
);
ALTER TABLE calendar
ADD CONSTRAINT PK_calendar
PRIMARY KEY (calDate);
INSERT INTO calendar VALUES ('2012-10-10', 1);
INSERT INTO calendar VALUES ('2012-10-11', 1);
INSERT INTO calendar VALUES ('2012-10-12', 1);
query:
select s.someCol, c.calDate from someTable s, calendar c;
You really have two options for what you are trying to do.
If your RDBMS supports it (I know SQL Server does, but I don't know any others), you can create a table-valued function which takes in a date range and returns a result set of all the discrete dates within that range. You would do a cartesian join between your table and the function.
You can create a static table of date values and then do a cartesian join between the two tables.
The second option will perform better, especially if you are dealing with large date ranges, however, that solution will not be able to handle arbitrary date ranges. But then, you should know your minimum date, and you can alway add more dates to your table as time goes on.
I am not very clear about your M table. Providing that you have such a table(M) with dates, following cross join will bring the results.
SELECT C.*, M.date FROM C CROSS JOIN M