I have a tables like this.
Name
Created
Frank
20210321
Jack
20210324
And expect output table:
Name
TaskID
Frank
Frank-20210321
Frank
Frank-20210322
Frank
Frank-20210323
Frank
Frank-20210324
Jack
Frank-20210324
Assume today is 20210324, and How to generate a new table?
What i have tried.
SELECT name, concat(name, "-", created)
And I know I can use
SELECT DATEDIFF('2021-03-25', '2021-03-21') as DAYS;
To calculate a different days.
But it just simple one task was generate per each line.
WITH RECURSIVE
cte AS ( SELECT CAST(MIN(Created) AS DATE) d FROM test
UNION ALL
SELECT d + INTERVAL 1 DAY FROM cte WHERE d < CURRENT_DATE )
SELECT test.name, CONCAT(test.name, '-', DATE_FORMAT(cte.d, '%Y%m%d')) TaskID
FROM cte
JOIN test ON cte.d >= test.Created
ORDER BY TaskID
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=1e086f9ce8ed6b32713f2932549f295c
Related
I have a user table like this,
name week_no year_no
fb 5 2021
twitter 1 2022
twitter 2 2022
twitter 3 2022
twitter 7 2022
youtube 21 2022
I want to find the names of users who login >= 3 consecutive weeks in the same year. The week numbers will be unique for each year. For example, in the above table we can see that user twitter is logged in week_no: 1, 2, 3 in the same year 2022 thereby satisfying the condition that I am looking for.
The output I am looking for,
name year_no
twitter 2022
You can create the sample table using,
CREATE TABLE test (
name varchar(20),
week_no int,
year_no int
);
INSERT INTO test (name, week_no, year_no)
VALUES ('fb', 5, 2021),
('twitter', 1, 2022),
('twitter', 2, 2022),
('twitter', 3, 2022),
('twitter', 7, 2022),
('youtube', 21, 2022);
I am new to SQL language and I read that group by can achieve that, can someone help in what function/query we have to use to achieve the same.
select * from test group by year_no, name;
Thank you for any help in advance.
A simple solution which will work on every MySQL version, without using windows function. Join the same table 3 times
SELECT t1.name,t1.year_no
FROM test t1
INNER JOIN test t2 ON t1.name=t2.name AND t1.year_no=t2.year_no
INNER JOIN test t3 ON t1.name=t3.name AND t1.year_no=t3.year_no
WHERE t2.week_no = t1.week_no + 1
AND t3.week_no = t1.week_no + 2
https://dbfiddle.uk/XjeXKUFE
You may define a unique groups for consecutive weeks in the same year and aggregate them as the following:
SELECT name, year_no
FROM
(
SELECT *,
week_no -
ROW_NUMBER() OVER (PARTITION by name, year_no ORDER BY week_no) grp
FROM test
) T
GROUP BY name, year_no, grp
HAVING COUNT(*) >= 3
ORDER BY name, year_no
See a demo.
Window function version.
demo
WITH cte AS (
SELECT
name,
week_no,
year_no,
lag(week_no) OVER (PARTITION BY name,
year_no ORDER BY year_no,
week_no) AS lag,
lead(week_no) OVER (PARTITION BY name,
year_no ORDER BY year_no,
week_no) AS lead
FROM
testuser
)
SELECT DISTINCT
name,
year_no
FROM
cte
WHERE
lead + lag = 2 * week_no;
i have a table
Id
Month
Salary
1
1
20
2
1
20
1
2
30
2
2
30
3
2
40
1
3
40
3
3
60
1
4
60
3
4
70
I was trying to remove some max month in each id . I was trying the following query
select * from Employee
where id , month not in ( select distinct id, max(Month) over(partition by id ) from Employee)
I cant understand what wrong with this query why cant i do this way. Is there any alternative way for this
Your method should work. The syntax is:
select *
from Employee
where (id, month) not in (select distinct id, max(Month) over(partition by id )
from Employee
)
I wouldn't recommend this approach. The window function is superfluous, when you just want aggregation:
select *
from Employee
where (id, month) not in (select id, max(Month)
from Employee
group by id
);
Or a correlated subquery seems more natural to me:
select e.*
from Employee e
where month < (select max(e2.Month)
from Employee e2
where e2.id = e.id
);
This has the advantage that it can use an index on Employee(id, month) and is probably the best performing way to write the query.
So I'm having an issue with what I expect is a very simple problem, but for the life of me I can't figure it out!
I have a table like this:
id name status date
1 bob good 01/01/2020
2 john good 01/01/2020
3 bob bad 02/01/2020
4 john good 02/01/2020
5 ben good 02/01/2020
I want to retrieve the latest record for each name.
I have tried the following:
SELECT name
,STATUS
,MAX(DATE)
FROM TABLE
GROUP BY name
ORDER BY MAX(DATE)
I thought this worked, however it is returning a record for bob, john and ben, but it is showing bobs date as 02/01/2020 but his status as "good" from the other record!
At a loss as to how to do this in the simplest way possible, all help is much appreciated!
Don't think of this as aggregation. Think of this as filtering!
Select t.name, t.status, t.date
from table t
where t.date = (select max(t2.date)
from table t2
where t2.name = t.name
);
You are not aggregating anything. Your result set just wants columns from one row, the row with the maximum date for each name. That is more like filtering than grouping.
With not exists:
select t.* from tablename t
where not exists (
select 1 from tablename
where name = t.name and date > t.date
)
The result is:
every row of the table for which there is not another row with the same name and later date.
For MySql 8.0+ you can use ROW_NUMBER() window function:
select t.id, t.name, t.status, t.date
from (
select *, row_number() over (partition by name order by date desc) rn
from tablename
) t
where t.rn = 1
Maria DB 10.2 apparently. – Ed Jones
SELECT DISTINCT name,
FIRST_VALUE(status) OVER (PARTITION BY name
ORDER BY date DESC) status,
MAX(date) OVER (PARTITION BY name) date
FROM table;
The index by (name, data) will increase the performance.
I have a table EMP with employees id and their hireyear. And I have to get the amount of hired employees in lets say the the years 2002 and 2000. The output table should als contain the amount of hired employees in the whole time.
So the last is easy. I just have to write:
SELECT COUNT(id) AS GLOBELAMOUNT FROM EMP;
But how do I count the amount of hired employees in 2002?
I could write the following:
SELECT COUNT(id) AS HIREDIN2002 FROM EMP WHERE YEAR = 2002;
But how do I combine this in one tuple with the data above?
Maybe I should group the data by Hireyear first and then count it? But can not really imagine how I count the data for several years.
Hope u guys can help me.
Cheers,
Andrej
Use conditional aggregation, e.g.:
SELECT COUNT(id) AS GLOBELAMOUNT,
COUNT(CASE WHEN YEAR=2000 THEN 1 END) AS HIREDIN2000,
COUNT(CASE WHEN YEAR=2002 THEN 1 END) AS HIREDIN2002
FROM EMP;
In Microsoft SQL Server (Transact-SQL) at least, you can use a windowed aggregate function like this:
Select Distinct
Year
,count(Id) over (Partition by Year) as CountHiredInYear
,count(Id) over () as CountTotalHires
From EMP
This gives something like:
Year | CountHiredInYear | CountTotalHires
2005 | 3 | 12
2006 | 4 | 12
2007 | 5 | 12
Another SQL Server specific approach is the With Rollup keyword.
Select Year
,count(Id) as CountHires
From Emp
Group by Year
With Rollup
This adds a summary line for each level of grouping, with the total value for that set of rows. So here, you'd get an extra row where Year was NULL, with the value 12.
You could use two (or more) inline queries:
SELECT
(SELECT COUNT(id) FROM EMP) AS GLOBELAMOUNT,
(SELECT COUNT(id) FROM EMP WHERE YEAR = 2002) AS HIREDIN2002
or a CROSS JOIN:
SELECT GLOBELAMOUNT, HIREDIN2002
FROM
(SELECT COUNT(id) AS GLOBELAMOUNT FFROM EMP) g CROSS JOIN
(SELECT COUNT(id) AS HIREDIN2002 FROM EMP WHERE YEAR = 2002) h
Hope all the SQL GURUS out there are doing great :)
I am trying to simulate LEAD() and LAG() functionality in SQL Server 2008.
This is my scenario: I have a temp table which is populated using the base query with the business logic for mileage. I want to calculate accumulated mileage for each user per day.
The temp table is setup using ROW_NUMBER(), so I have all the data needed in the temp table except the accumulated mileage.
I have tried using a CTE with the base query and self joining with itself and couldn't get it working. I am attaching the screen shot for the same.
Any help/suggestion would be appreciated.
You are on the right track by joining the table to itself. I included 2 methods of doing this below that should work fine here. The first trick is in your ROW_NUMBER, be sure to partition by the user id and sort by the date. Then you can use either an INNER JOIN with aggregation or CROSS APPLY to build your running totals.
Setting up the data with the partitioned ROW_NUMBER():
DECLARE #Data TABLE (
RowNum INT,
UserId INT,
Date DATE,
Miles INT
)
INSERT #Data
SELECT
ROW_NUMBER() OVER (PARTITION BY UserId
ORDER BY Date) AS RowNum,
*
FROM (
SELECT 1, '2015-01-01', 5
UNION ALL SELECT 1, '2015-01-02', 6
UNION ALL SELECT 2, '2015-01-01', 7
UNION ALL SELECT 2, '2015-01-02', 3
UNION ALL SELECT 2, '2015-01-03', 2
) T (UserId, Date, Miles)
Use INNER JOIN with Aggregation
SELECT
D1.UserId,
D1.Date,
D1.Miles,
SUM(D2.Miles) AS [Total]
FROM #Data D1
INNER JOIN #Data D2
ON D1.UserId = D2.UserId
AND D2.RowNum <= D1.RowNum
GROUP BY
D1.UserId,
D1.Date,
D1.Miles
Use CROSS APPLY for the running total
SELECT
UserId,
Date,
Miles,
Total
FROM #Data D1
CROSS APPLY (
SELECT SUM(Miles) AS Total
FROM #Data
WHERE UserId = D1.UserId
AND RowNum <= D1.RowNum
) RunningTotal
Output is the same for each method:
UserId Date Miles Total
----------- ---------- ----------- -----------
1 2015-01-01 5 5
1 2015-01-02 6 11
2 2015-01-01 7 7
2 2015-01-02 3 10
2 2015-01-03 2 12