Table1
id
hour
date
tableValue1
tableValue2
1
3
2020-05-29
123
145
2
2
2020-05-29
1500
3400
Table2:
id
hour
date
tableValue3
tableValue4
1
1
2020-05-29
4545
3697
2
3
2020-05-29
5698
2896
Table3:
id
hour
date
tableValue5
tableValue6
1
2
2020-05-29
7841
5879
2
1
2020-05-29
1485
3987
I want to select multiple columns from different tables with one query.
Expected Output:
hour
tableValue1
tableValue3
tableValue5
1
0
4545
1485
2
1500
0
7841
3
123
5698
0
I've tried this query without success:
SELECT hour , tableValue1 WHERE date = "2020-05-29" AND hour BETWEEN 0 AND 10 FROM table1
UNION ALL
SELECT hour , tableValue3 WHERE date = "2020-05-29" AND hour BETWEEN 0 AND 10 FROM table2
UNION ALL
SELECT hour , tableValue5 WHERE date = "2020-05-29" AND hour BETWEEN 10 AND 10 FROM table3
I'm getting instead the following:
hour
tableValue1
3
123
2
1500
1
4545
3
5698
2
5879
1
3987
The columns tables have in common are hour and date, do I need to redesign database structure to link the tables, so that I can use JOIN command, but how? Or is there a sql command to select multiple column from multiple tables?
There are a couple of issues in your code:
your WHERE clause should be found after the FROM clause in your subqueries
you want different columns, but you associate only one column for each of your table: if you want three columns, each of your subqueries should return three columns
your rows are not ordered because you're missing an ORDER BY clause at the end of your code.
your rows are not aggregated to remove the zeroes in excess: in that case it is sufficient to apply a MAX aggregation function for each relevant field, partitioning on the "hour" field
WITH cte AS (
SELECT hour,
tableValue1,
0 AS tableValue3,
0 AS tableValue5
FROM table1
WHERE date = "2020-05-29" AND hour BETWEEN 0 AND 10
UNION ALL
SELECT hour,
0 AS tableValue1,
tableValue3,
0 AS tableValue5
FROM table2
WHERE date = "2020-05-29" AND hour BETWEEN 0 AND 10
UNION ALL
SELECT hour,
0 AS tableValue1,
0 AS tableValue3,
tableValue5
FROM table3
WHERE date = "2020-05-29" AND hour BETWEEN 0 AND 10
ORDER BY hour
)
SELECT hour,
MAX(tableValue1) AS tableValue1,
MAX(tableValue3) AS tableValue3,
MAX(tableValue5) AS tableValue5
FROM cte
GROUP BY hour
Check the demo here.
You must introduce empty columns in first query
SELECT hour , tableValue1, 0 tableValue3, 0 tableValue5 FROM table1 WHERE date = "2020-05-29" AND hour BETWEEN 0 AND 10
UNION ALL
SELECT hour , 0, tableValue3, 0 FROM table2 WHERE date = "2020-05-29" AND hour BETWEEN 0 AND 10
UNION ALL
SELECT hour , 0,0 tableValue5 FROM table3 WHERE date = "2020-05-29" AND hour BETWEEN 10 AND 10
Related
there are many devices and while using it will upload data every some seconds or minutes.
I want to get the sections of date-time that the device is in use
Id date-time value
0 2021-07-08 14:46:46 1
1 2021-07-08 14:47:47 5
2 2021-07-08 14:48:48 2
3 2021-07-08 14:49:49 4
4 2021-07-08 15:30:01 7
5 2021-07-08 15:30:46 4
6 2021-07-08 15:30:46 4
7 2021-07-08 15:50:04 4
8 2021-07-08 15:50:05 6
can it be true that group the data by an interval?
let us consider interval = 1 minutes
then group the data which the minus of the two date-time is more than 1 minutes.
then Id=0 or Id=1 or Id=2 or Id=3 is one group and Id=4 and Id=5 and Id=6 and Id=7 and Id=8 is another group
what I want is the group is a nearly date-time.
If the difference between two records is more than 1 minute then they are in two groups. If not they are in the same groups.
which means in the same group time1 will be smaller than 1 minutes to one of the other time.
If the time difference is 1 or 10 minutes larger than the previous record it will belong to a new groups
and I am using MYSQL
You can use lag window function to obtain previous date_time.
One way to calculate the time difference in seconds is to convert timestamp type to integer by unix_timestamp function.
Make a newgroup flag which equals one if and only if the difference from the previous record is larger than 60*10 seconds (10 minutes).
Cumulative sum of newgroup would become the section group ID.
with tmp AS (
SELECT
*,
coalesce(unix_timestamp(date_time) - unix_timestamp(lag(date_time) over (ORDER BY date_time)), 0) > 60*10 AS newgroup
FROM
tbl
)
,tmp2 AS (
SELECT
*,
sum(newgroup) over (ORDER BY date_time) AS groupid
FROM
tmp
)
SELECT * FROM tmp2
This query would get:
id date_time value newgroup groupid
0 2021-07-08 14:46:46 1 0 0
1 2021-07-08 14:47:47 5 0 0
2 2021-07-08 14:48:48 2 0 0
3 2021-07-08 14:49:49 4 0 0
4 2021-07-08 15:30:01 7 1 1
5 2021-07-08 15:30:46 4 0 1
6 2021-07-08 15:30:46 4 0 1
7 2021-07-08 15:50:04 4 1 2
8 2021-07-08 15:50:05 6 0 2
Hmmm . . . It sounds like you are looking for gaps to defines groups that are related, and the gaps are determined by the interval.
In pseudo-SQL, this might look like:
select min(date_time), max(date_time), count(*), avg(value)
from (select t.*,
sum(case when prev_date_time > date_time - interval '1 minute' then 0 else 1 end) over (order by date_time) as grp
from (select t.*,
lag(date_time) over (order by date_time) as prev_date_time
from t
) t
) t
group by grp;
For example
ID TID DATE BALANCE
1 24 02-11-2018 198
2 2 08-11-2018 199
2 3 05-11-2018 0
4 13 26-11-2018 115
4 14 28-11-2018 113
Balance for Nov-18 should be displayed as below
ID BALANCE
1 198
2 0
3 0
4 113
for id-3, 0 should be displayed since there is no balance for these id in the month of November for id -1,2 & 4 min balance should be displayed.
Most of what you want can be obtained by a simple GROUP BY and some date formatting:
select a.id,
date_format(date, '%Y-%m') as month,
coalesce(min(balance), 0) as min_balance
from accounts a
left outer join balances b
on b.id = a.id
group by a.id, date_format(date, '%Y-%m')
order by a.id, date_format(date, '%Y-%m')
Here we're using the date_format function to extract the year and month from the date value, and then grouping the results by id and the formatted date. This produces the result
id month min_balance
1 2018-11 198
2 2018-11 0
3 0
4 2018-11 113
It seems apparent that there must be another table which contains all the account values, so I added a table named accounts with these values. Change this if needed.
db<>fiddle here
The table ShopOrder's columns include:
id shopid starttime endtime
1 123 2018-04-27 2018-04-28
2 234 2018-04-23 2018-04-30
3 189 2018-05-01 2018-05-30
4 321 2018-05-01 2018-05-29
I wan't to query for valid shop counts between two days and count by each day of latest month,the valid shop counts means the starttime<= $curDate <= endtime,and curDate is a variable of the each day of the leatest month.
Today is 2018-04-27,so the query result should be:
day count
2018-04-27 2
2018-04-26 1
2018-04-25 1
2018-04-24 1
2018-04-23 1
2018-04-22 0
2018-04-21 0
……………………………………
2018-03-26 0
how can i achieve this use MySQL?
Converting between start and end dates and a range of individual dates is a challenge in SQL because you probably don't have a table which contains a row for each day in the current month to join on.
You can fill a table with dates using logic from one of the answers in How to populate a table with a range of dates?.
Or, since the range you want to produce is quite short, you can just create it manually in your query. It isn't completely clear what you mean by "the latest month" since your example ranges from 26 April to 27 March, but if the last 30 days is reasonable enough, you can use UNION to create this list.
Comparing date ranges discusses how to test whether a date is in a particular range or not, so putting the two things together gives you something like
SELECT
DATE_SUB(DATE(NOW()), INTERVAL days_ago.days DAY) day,
COUNT(ShopOrder.id) count
FROM
(SELECT 0 days UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION
SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION
SELECT 10 UNION SELECT 11 UNION SELECT 12 UNION SELECT 13 UNION SELECT 14 UNION
SELECT 15 UNION SELECT 16 UNION SELECT 17 UNION SELECT 18 UNION SELECT 19 UNION
SELECT 20 UNION SELECT 21 UNION SELECT 22 UNION SELECT 23 UNION SELECT 24 UNION
SELECT 25 UNION SELECT 26 UNION SELECT 27 UNION SELECT 28 UNION SELECT 29)
AS days_ago
LEFT JOIN ShopOrder
ON DATE_SUB(DATE(NOW()), INTERVAL days_ago.days DAY) <= ShopOrder.endtime
AND DATE_SUB(DATE(NOW()), INTERVAL days_ago.days DAY) >= ShopOrder.starttime
GROUP BY days_ago.days;
I have been looking at several different questions related to hourly average queries but I could not find any that addresses the following.
I have a log table that keeps track on how many times a page is accessed by a user:
ID USERID PAGEID SECNO DATE
1 123 120 14 6/08/2013 10:07:29 AM
1 124 438 1 6/08/2013 11:00:01 AM
1 123 211 18 6/09/2013 14:07:59 PM
1 123 120 14 6/10/2013 05:07:18 PM
1 124 312 4 6/10/2013 08:04:32 PM
1 128 81 54 6/11/2013 07:02:15 AM
and I am trying to get two different queries. One that looks like this:
HOURLY Count Average
12am 0 0
1am 0 0
2am 0 0
3am 0 0
4am 0 0
5am 1 0
6am 0 0
7am 1 0
8am 0 0
9am 0 0
10am 1 0
11am 1 0
12pm 0 0
1pm 0 0
2pm 1 0
3pm 0 0
4pm 0 0
5pm 1 0
6pm 0 0
7pm 0 0
8pm 1 0
9pm 0 0
10pm 0 0
11pm 0 0
The second query like this:
DAY PERCENTAGE
Monday 10%
Tuesday 16%
Wednesday 14%
Thursday 22%
Friday 21%
Saturday 14%
Sunday 3%
**Please notice that the average value is just a sample
So far for the first query I have something like this:
SELECT
HOUR(date) AS hourly,
Count(*)
FROM
logs
GROUP BY
hourly
I tried adding AVG() after Count() but did not work.
My log table does not have data for every single hour but i still need to display all the hours on my report. if hour empty, then value 0. Any ideas how could I achieve that?
Try this for the first query:
SELECT
h.hour,
IFNULL(tmp.the_count,0),
IFNULL(tmp.the_avg,0)
FROM
hourly h
LEFT JOIN (
SELECT
hourly,
SUM(visits) the_count,
SUM(visits)/COUNT(DISTINCT userid) as the_avg
FROM (
SELECT
HOUR(date) AS hourly,
COUNT(*) as visits,
userid
FROM
logs
GROUP BY
hourly,
userid
) as tmp
GROUP BY
hourly
) as tmp
ON tmp.hourly = h.hour
Try this for the second query:
SELECT
theday,
IFNULL(percentage,0) as percentage
FROM (
SELECT DATE_FORMAT('2013-06-16','%W') as theday UNION
SELECT DATE_FORMAT('2013-06-16' - INTERVAL 1 DAY,'%W') as theday UNION
SELECT DATE_FORMAT('2013-06-16' - INTERVAL 2 DAY,'%W') as theday UNION
SELECT DATE_FORMAT('2013-06-16' - INTERVAL 3 DAY,'%W') as theday UNION
SELECT DATE_FORMAT('2013-06-16' - INTERVAL 4 DAY,'%W') as theday UNION
SELECT DATE_FORMAT('2013-06-16' - INTERVAL 5 DAY,'%W') as theday UNION
SELECT DATE_FORMAT('2013-06-16' - INTERVAL 6 DAY,'%W') as theday
) as weekt
LEFT JOIN (
SELECT
DATE_FORMAT(date,'%W') AS daily,
(COUNT(*)/(SELECT COUNT(*) FROM logs))/100 as percentage
FROM
logs
WHERE
date >= '2013-06-10'
AND date <= '2013-06-16'
GROUP BY
daily
) as logdata
ON logdata.daily = weekt.theday
SQL has no way to "create" an hour out of nothing. So the simple trick is to have a table numbers (number int) with the numbers you need (may be 1- 31 to be ready for month, or 1-366 for year). That table you can left join with your data in the kind of
select n.number as hour, count(*) as cnt
from numbers as n
left join logtable as l
on hour(l.date) = n.number
group by n.number
You could "simulate" it without a table, but there are several occasions where that table is helpful.
I need a column from row value.
I have two table.
Table 1 : working_day Contains list of all working day date.
date
--------
2013-03-30
2013-03-29
2013-03-28
Table 2 : entry contains each employee in and out time.
id In Out Date
1 9 0 2013-03-30
2 8 0 2013-03-30
3 7 0 2013-03-30
1 8 18 2013-03-29
2 9 16 2013-03-29
3 6 20 2013-03-29
4 12 15 2013-03-29
Expected Output :
ID 29-03-2013_IN 29-03-2013_Out 30-03-2013_In
1 8 18 9
2 9 16 8
3 6 20 7
4 12 15 0
Tried :
SELECT id,
Case condition1 for 29_in, // I don't know which condition suite here.
Case condition1 for 29_out,
Case condition1 for 30_in
FROM entry
WHERE DATE
IN (
SELECT *
FROM (
SELECT DATE
FROM working_day
ORDER BY DATE DESC
LIMIT 0 , 2
)a
)
You could try something like that:
select
e.id,
(SELECT `in` FROM entry WHERE id = e.id AND date = '2013-03-30') as '2013-03-30_in',
(SELECT `in` FROM entry WHERE id = e.id AND date = '2013-03-29') as '2013-03-29_in',
(SELECT `out` FROM entry WHERE id = e.id AND date = '2013-03-29') as '2013-03-29_out'
from entry e
group by e.id;
Here is Demo
IMO you should do this in application instead of SQL