I'm trying to produce a query that shows every order AND adds an additional column which shows if the customer has previously placed an order by NAME, ADDRESS or USER_ID, without grouping results.
I've tried:
SELECT *, COUNT(`NAME`) c FROM `salesorders` GROUP BY `NAME` HAVING c > 0
This shows all customer orders, however if the customer has previously ordered before, the customers name only shows once due to the grouping.
e.g.
Name, Date, Previous_Orders
John Smith, 2019-11-28, 2
Lisa Thomas, 2019-11-20, 1
I would like it show both occurrences:
John Smith, 2019-11-28, 2
Lisa Thomas, 2019-11-20, 1
John Smith, 2019-05-07, 2
salesorders columns;
Name
User_ID
Date
Lets say the new column is Previous_Orders
For MariaDB 10.4.6 you can use COUNT() window function:
select *,
count(full_add) over (partition by full_add) Previous_Orders
from salesorders;
If the column Name is unique, then you can replace user_id with Name if you prefer.
If by Previous_Orders you mean the number of orders up to the date in the current row, then change to:
select *,
count(full_add) over (partition by full_add order by date) Previous_Orders
from salesorders;
See the demo.
You could use a join with the subquery for count group by name
SELECT s.*, t.c
FROM `salesorders` s
INNER JOIN
(SELECT name, COUNT(*) c
FROM salesorders
GROUP BY name) t ON t.name = s.name
WHERE t.c > 0
Related
I have a table with name 'employees', I want to group the rows by their designation only till it changes.
please look at the sample table.
Id Name Designation Employee Id Last Modified
1 John General Manager 1 15-10-2001
2 John General Manager 1 05-11-2001
3 John Product Manager 1 06-11-2001
4 John Product Manager 1 15-11-2001
5 John General Manager 1 30-12-2001
Expected Output
Employee Id Name Designation Valid From Valid to
1 John General Manager 15-10-2001 05-11-2001
1 John Product Manager 06-11-2001 15-12-2001
1 John General Manger 16-12-2001 30-12-2001
I tried using group by clause, but it is giving me only 2 rows (General Manager, Product Manager).
But I want an output as shown in the table from which I can solve a bigger problem that involves starting date and ending date for each role.
Thanks in advance!.
Here you go:
WITH cte1 AS(
SELECT EmployeeId,
LastModified,
Designation,
LEAD(Designation) OVER (ORDER BY EmployeeId, LastModified) AS NextDesg,
LAG(LastModified) OVER (ORDER BY EmployeeId, LastModified) AS PrevLm
FROM mytable),
cte2 AS (
SELECT *,
ROW_NUMBER()
OVER (PARTITION BY EmployeeId ORDER BY LastModified) AS NewGroup
FROM cte1
WHERE (Designation != NextDesg OR NextDesg IS NULL)
ORDER BY LastModified)
SELECT t.EmployeeId, t.Name, t.Designation,
MIN(t.LastModified) AS "Valid From",
MAX(t.LastModified) AS "Valid To",
COALESCE(c1.NewGroup, c2.NewGroup) AS NwGr
FROM mytable t
LEFT JOIN cte2 c1
ON t.EmployeeId=c1.EmployeeId
AND t.LastModified=c1.LastModified
LEFT JOIN cte2 c2
ON t.EmployeeId=c2.EmployeeId
AND t.LastModified=c2.PrevLm
GROUP BY t.EmployeeId, t.Name, t.Designation, NwGr;
The main idea here is to assign each set as a new group, then use that to group by the results.
We start by doing common table expression twice. The first one is:
WITH cte1 AS(
SELECT EmployeeId,
LastModified,
Designation,
LEAD(Designation) OVER (ORDER BY EmployeeId, LastModified) AS NextDesg,
LAG(LastModified) OVER (ORDER BY EmployeeId, LastModified) AS PrevLm
FROM mytable)
I'm using LEAD() to get the next Designation value to be used as a delimiter and LAG() to get the previous LastModified value to be used in ON for the second LEFT JOIN in the final query.
The second cte:
cte2 AS (
SELECT *,
ROW_NUMBER()
OVER (PARTITION BY EmployeeId ORDER BY LastModified) AS NewGroup
FROM cte1
WHERE (Designation != NextDesg OR NextDesg IS NULL)
ORDER BY LastModified)
Here is where I used the value returned using LEAD() the first cte to identify the change in Designation then I add ROW_NUMBER() function to generate as NewGroup.
Then in the final query:
...
FROM mytable t
LEFT JOIN cte2 c1
ON t.EmployeeId=c1.EmployeeId
AND t.LastModified=c1.LastModified
LEFT JOIN cte2 c2
ON t.EmployeeId=c2.EmployeeId
AND t.LastModified=c2.PrevLm
...
I did LEFT JOIN from the main table with cte2 twice, the first one is matching by EmployeeId and LastModified of both and the second one is matching by EmployeeID and the previous LastModified value obtained from the LAG() function.
Demo fiddle
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 called 'customers':
id | name | age
1 | john | 35
2 | paul | 22
3 | ana | 26
4 | mark | 19
5 | jack | 29
i want to select the name and max age, the name and min age... something like:
john 35 mark 19
is it possible?
The below query will give you the min and max on one row as requested. If there are multiple matches for min/max you will get multiple rows. Depending on the SQL engine you use, the syntax to limit to one row is different.
SELECT cMax.Name, cMax.Age, cMin.Name, cMin.Age
FROM customers cMin
JOIN customers cMax ON
cMax.Age = (SELECT MAX(Age) FROM customers)
WHERE cMin.Age = (SELECT MIN(Age) FROM customers)
There are different types of joins (e.g. INNER, OUTER, CROSS); however, for your question it doesn't much matter which you use.
Yes, you can do it,
select name, age from customers
where age in (select max(age)
from customers union select min (age)from customers)
try this
select name, age from customers where age=(select max(age) from customers) union
select name, age from customers where age=(select min(age) from customers)
If you want them on the same row:
select cmin.*, cmax.*
from (select name, age as minage
from customers
order by age asc
fetch first 1 row only
) cmin cross join
(select name, age as maxage
from customers
order by age desc
fetch first 1 row only
) cmax;
fetch first 1 row only is standard syntax for returning only the first row of the result set. Some databases have bespoke syntax, such as limit or select top (1).
Try using this query to show MAX age:-
select * from customers where age=(select max(age) from customers);
To show MIN age use the below Query:-
select * from customers where age=(select min(age) from customers);
You could use a cross join, which will put the two query ouputs next to one another. Building off Rodrigo's queries:
select
max_cust.name,
max_cust.age,
min_cust.name,
min_cust.age
from (select name, age from customers where age=(select max(age) from customers)) as max_cust
cross join (select name, age from customers where age=(select min(age) from customers)) as min_cust
It's maybe not the most performant but it gets the right shape. Be wary of cross joins when the tables don't have exactly 1 row, as it creates a cartesian product of the rows in the tables being joined.
Here is my PEOPLE table where I store name, surname, DOB (date of birth) and some other data. In a new query I need to add additional column with the condition for people with the same Name & Surname combination. Here is the condition:
Go through each group of people with the same Name & Surname combinations, in additional field mark those, whose DOB is not maximum and not minimum (compared only to same Name & Surname)
If there is only 1 or 2 occurrences of same Name & Surname, mark them anyway
Here is the result of a query
Explanation:
John Doe marked as met only once
Tom Taylor marked as met only twice
Alice Smith and Bob Brown marked everywhere except records with min and max DOB
Please help to form SQL query for the desired output. Here is my understanding (guessing)
Get list of unique Name&Surname pairs, ( where occurrences >2 ??? )
For each unique pair find rows with min and max DOB (avoid them)
In a new CheckBox column mark those that are left (not extremum)
First group by name, surname to get the number of occurrences and min and max dob of each name and surname and join the results to the table.
With a CASE statement apply the conditions:
select
p.*,
case
when g.counter in (1, 2) then 'mark'
else case
when p.dob not in (g.mindob, g.maxdob) then 'mark'
end
end Checkbox
from peaople inner join (
select
name, surname,
count(*) counter,
min(dob) mindob,
max(dob) maxdob
from people
group by name, surname
) g on g.name = p.name and g.surname = p.surname
Use window functions in MySQL 8+:
select p.*,
(case when count(*) over (partition by name, surname) <= 2
then 'mark'
when row_number() over (partition by name, surname order by dob) > 1 and
row_number() over (partition by name, surname order by dob desc) > 1
then 'mark'
end) as checkbox
from people p;
Note: If there are duplicates for the earliest or latest birthdate, this only excludes one of them. If you want to exclude all of them, use rank() instead of row_number().
I have a table, the table structure of which is as follows
id service_type pincode
---------------------------------
1 B 695582
2 D 676102
3 P 685584
4 B 685608
I will get two different pincodes from the front end. say for example 695582 and 685608. I need to get the service_types at these pincodes with single query and the results should be a single row. I have tried a UNION query, but the results are in two different row.
select 'buyer' name, service_type from tm_location_carrier_lnk where pincode=695582
UNION
select 'seller' name, service_type from tm_location_carrier_lnk where pincode=685608
The result of the above query is as follows
name service_type
------------------
buyer B
seller B
How can i get results in a single row as follows
Buyer Seller
B B
Why not doing
SELECT (select service_type from tm_location_carrier_lnk where pincode=695582 LIMIT 1) as Buyer, (select service_type from tm_location_carrier_lnk where pincode=685608 LIMIT 1) as Seller;
Here you can find a working example: http://sqlfiddle.com/#!9/e01426/1
edit: added LIMIT 1 for avoiding duplicated pincodes.
One solution assuming unique pincodes and specific to your request:
Select
(select max(service_type)
from tm_location_carrier_lnk
where pincode=695582) as Buyer
, (select max(service_type)
from tm_location_carrier_lnk
where pincode=685608) as Seller;