How to get dates based on months that appear more than once? - sql

I'm trying to get months of Employees' birthdays that are found in at least 2 rows
I've tried to unite birthday information table with itself supposing that I could iterate through them abd get months that appear multiple times
There's the question: how to get birthdays with months that repeat more than once?
SELECT DISTINCT e.EmployeeID, e.City, e.BirthDate
FROM Employees e
GROUP BY e.BirthDate, e.City, e.EmployeeID
HAVING COUNT(MONTH(b.BirthDate))=COUNT(MONTH(e.BirthDate))
UNION
SELECT DISTINCT b.EmployeeID, b.City, b.BirthDate
FROM Employees b
GROUP BY b.EmployeeID, b.BirthDate, b.City
HAVING ...
Given table:
| 1 | City1 | 1972-03-26|
| 2 | City2 | 1979-12-13|
| 3 | City3 | 1974-12-16|
| 4 | City3 | 1979-09-11|
Expected result :
| 2 | City2 |1979-12-13|
| 3 | City3 |1974-12-16|

Think of it in steps.
First, we'll find the months that have more than one birthday in them. That's the sub-query, below, which I'm aliasing as i for "inner query". (Substitute MONTH(i.Birthdate) into the SELECT list for the 1 if you want to see which months qualify.)
Then, in the outer query (o), you want all the fields, so I'm cheating and using SELECT *. Theoretically, a WHERE IN would work here, but IN can have unfortunate side effects if a NULL comes back, so I never use it. Instead, there's a correlated sub=query; which is to say we look for any results where the month from the outer query is equal to the months that make the cut in the inner (correlated sub-) query.
When using a correlated sub-query in the WHERE clause, the SELECT list doesn't matter. You could put 1/0 and it won't throw an error. But I always use SELECT 1 to show that the inner query isn't actually returning any results to the outer query. It's just there to look for, well, the correlation between the two data sets.
SELECT
*
FROM
#table AS o
WHERE
EXISTS
(
SELECT
1
FROM
#table AS i
WHERE
MONTH(i.Birthdate) = MONTH(o.Birthdate)
GROUP BY
MONTH(i.Birthdate)
HAVING
COUNT(*) > 1
);

Seems to be an odd requirement.
This might help with some tweaks. Works in Oracle.
SELECT DATE FROM TABLE WHERE EXTRACT(MONTH FROM DATE)=EXTRACT(MONTH FROM SOMEDATE);

Give this a try and you may be able to dispense with your UNION:
SELECT
EmployeeId
, City
, BirthDate
FROM Employees
GROUP BY
EmployeeId
, City
, BirthDate
HAVING COUNT(Month(BirthDate)) > 2

Here is another approach using GROUP_CONCAT. It's not exactly what you're looking for but it might do the job. Eric's approach is better though. (Note: This is for MySQL)
SELECT GROUP_CONCAT(EmployeeID) EmployeeID, BirthDate, COUNT(*) DupeCount
FROM Employees
GROUP BY MONTH(BirthDate)
HAVING DupeCount> 1;

Related

SQL Window Function to get addresses with more than 1 unique last name present (Snowflake)

I have a Snowflake table which includes addresses, state, first names and last names.
I would like to get a query that shows me only the addresses where more than 1 individual with a different last name is present.
So for example, assume that I have
address | fname | lname |State
10 lake road| John | Smith |FL
10 lake road| Julie | Gallagher|FL
3 gator cove| Jack | Hoyt |FL
3 gator cove| Debra | Hoyt |FL
I would like the query to return only 1 row in that example: 10 lake road. Because it's the only house where there is more than 1 unique last name present.
I am currently using
SELECT distinct a.address, a.fname, a.lname, a.state
FROM clients_addresses a
WHERE a.state = 'FL'
qualify count(1) over( partition by a.lname) > 1
order by a.address
However, this is just returning the addresses where there is more than 1 person, it doesn't care if the last name is repeated. That's what I'm trying to avoid.
I can't quite understand where the query is going wrong. Snowflake doesn't like using any distinct keyword after the initial select, and even if I use it, it only returns 1 occurrence of each address, but it's still just addresses with more than 1 person, even if there was only 1 last name in the address.
It doesn't need to involve the keyword "qualify", I know Snowflake also accepts other things such as subselects that might help with this problem.
I would like the query to return only 1 row in that example: 10 lake road.
This sounds like aggregation:
SELECT a.address, count(*)
FROM clients_addresses a
WHERE a.state = 'FL'
GROUP BY a.address
HAVING COUNT(DISTINCT a.lname) > 1;
If you want the original rows (which is not what your question asks for), you can use:
SELECT a.*
FROM clients_addresses a
WHERE a.state = 'FL'
QUALITY COUNT(DISTINCT a.lname) OVER (PARTITION BY a.address) > 1;

How can I delete completely duplicate rows from a query, without having a unique value for it?

I'm having an issue getting information from an MS Access Database table. I need a count of a code but I don't have to take into account duplicate rows, which means that I need to delete all duplicate rows.
Here's an example to illustrate what I need:
Code | Name
12 | George
20 | John
12 | George
33 | John
I will need first to delete both rows with the same code, and then I need a count for the name the rest of the table data for example this will be the result that I'm expecting:
Name | Count
John | 2
I already have a query that does that for me, but is taking around 1 hour to get me around 5000 rows and I need something more efficient. My query:
select name, count(*) from Table
where name = '" + input_name + "'
and code in (select code from Table group by code
having count(code) = 1)
group by name
order by count(name) desc;
I would appreciate any suggestion.
Rather than using in, I might suggest filtering the original dataset in a subquery, e.g.:
select u.name, count(*)
from (select t.code, t.name from yourtable t group by t.code, t.name having count(*) = 1) u
group by u.name
Here, change yourtable to the name of your table.

Sum of two columns alongside separate select statement

I am working on an SQL task and I cannot figure out how to get the sum of two columns from the same table while displaying information from another table.
I have tried multiple things and have spent probably about two hours trying to figure this out.
I have two tables: Employees and Fuel. I displayed all of the employee's information.First SQL statement I had to make:
SELECT firstname, lastname, title, registrationyear, make, model FROM Employees ORDER BY make;
My Employees table has the following columns: firstname, lastname, employeeid, make, model, registrationyear, title
My Fuel table has the following columns: currentprice, fueltype, fuelcost, mileage, mileagecount, fuelamount, employeeid, date
My instructions state: "A list that shows what cars the employees currently use (first SQL statement I made, so this one is DONE!)
Like the above report but also the total amount of kilometers that the employees have driven and the total fuel cost." (this is the task that I am trying to make a statement for)
I have tried using LIKE, UNION, UNION ALL, etc. and the best that I have been able to do is listing the employee information and the totals ON TOP of the information instead of in two separate columns of their own alongside the other data in the query.
I am really stuck here. Could anyone please help me?
This second task is muck more complex than the first one.
First of all, combining in a single row the columns from two or more tables is what join is for, so you will have to join the two tables based on employeeid. This will return you a table like this
employeeid | other emp fields | fuel date | other fueld fields
1 | ... | 01/01/2017 | ...
1 | ... | 01/02/2017 | ...
2 | ... | 01/01/2017 | ...
2 | ... | 02/01/2017 | ...
2 | ... | 04/03/2017 | ...
From here, you want the data from each employee combined with the sum of the rows from fuel related to that employee, and that's what group by is for.
When using group by you define a set of columns that defines the grouping criteria; everything else in your select statement will have to be grouped somehow (in your case with a sum), so that the columns in the group by stay unique.
Your final query would look like this
select t1.firstname, t1.lastname, t1.title, t1.registrationyear, t1.make, t1.model,
sum(t2.mileage) as total_milege,
sum(t2.fuelcost * t2.fuelamount) as total_fuel_cost
from Employees t1
join Fuel t2
on t1.employeeid = t2.employeeid
group by t1.firstname, t1.lastname, t1.title, t1.registrationyear, t1.make, t1.model
Note: I don't know the difference between mileage and mileagecount, so the part of my query involving those fields may need some tweaking.
You can use Inner join & Group By clause as mentioned below. Let me know if you mean something else.
SELECT A.firstname, A.lastname, A.title, A.registrationyear, A.make, A.model,
SUM(B.Column_Having_Kilometer_Driven_Value)
FROM
Employee A
INNER JOIN Fuel B ON A.EmployeeID = B.EmployeeID
Group By A.EmployeeID, A.firstname, A.lastname, A.title, A.registrationyear, A.make, A.model

How to get a correlated subquery as column

I dont know how I can do this sql query, probably its simple but I don't know how i can do it.
I have 2 tables:
Table_Articles:
COD NAME
1 Bottle
2 Car
3 Phone
Table_Articles_Registered
COD_ARTICLE DATE
1 05/11/2014
1 06/11/2014
1 07/11/2014
2 08/11/2014
2 09/11/2014
3 05/11/2014
I want take in the table Table_Articles_Registered the row with the MAX date , finally I want get this result:
COD NAME DATE
1 Bottle 07/11/2014
2 Car 09/11/2014
3 Phone 05/11/2014
I need use the sencente like this. The problem its in the subquery. Later I use other inner join in the sentence, this is only a fragment.
select
_Article.Code,
_Article.Description ,
from Tbl_Articles as _Article left join
(
select top 1 *
from ArticlesRegisterds where DATE_REGISTERED <= '18/11/2014'
order by DATE_REGISTERED
)
as regAux
on regAux.CODE_ARTICLE= _Article.CODE
I dont know how can I connect the field CODE_ARTICLE in the table ArticlesRegisterds with the first query.
I think this is a basic aggregation query with a join:
select a.cod, a.name, max(ar.date) as date
from Artiles a join
ArticlesRegisterds ar
on ar.cod_article = a.cod
group by a.cod, a.name
Try this:-
SELECT TAR.COD_ARTICLE, TA.NAME, MAX(TAR.DATE)
FROM Table_Articles_Registered TAR JOIN
Table_Articles.TA ON TAR.COD_ARTICLE = TA.COD
GROUP BY TAR.COD_ARTICLE, TA.NAME;
Can't you just do this?:
SELECT
Table_Articles.COD,
Table_Articles.NAME,
(
SELECT MAX(Table_Articles_Registered.DATE)
FROM Table_Articles_Registered
WHERE Table_Articles.COD_ARTICLE=Table_Articles.COD
) AS DATE
FROM
Table_Articles

How to concatenate rows delimited with comma using standard SQL?

Let's suppose we have a table T1 and a table T2. There is a relation of 1:n between T1 and T2. I would like to select all T1 along with all their T2, every row corresponding to T1 records with T2 values concatenated, using only SQL-standard operations.
Example:
T1 = Person
T2 = Popularity (by year)
for each year a person has a certain popularity
I would like to write a selection using SQL-standard operations, resulting something like this:
Person.Name Popularity.Value
John Smith 1.2,5,4.2
John Doe NULL
Jane Smith 8
where there are 3 records in the popularity table for John Smith, none for John Doe and one for Jane Smith, their values being the values represented above. Is this possible? How?
I'm using Oracle but would like to do this using only standard SQL.
Here's one technique, using recursive Common Table Expressions. Unfortunately, I'm not confident on its performance.
I'm sure that there are ways to improve this code, but it shows that there doesn't seem to be an easy way to do something like this using just the SQL standard.
As far as I can see, there really should be some kind of STRINGJOIN aggregate function that would be used with GROUP BY. That would make things like this much easier...
This query assumes that there is some kind of PersonID that joins the two relations, but the Name would work too.
WITH cte (id, Name, Value, ValueCount) AS (
SELECT id,
Name,
CAST(Value AS VARCHAR(MAX)) AS Value,
1 AS ValueCount
FROM (
SELECT ROW_NUMBER() OVER (PARTITION BY Name ORDER BY Name) AS id,
Name,
Value
FROM Person AS per
INNER JOIN Popularity AS pop
ON per.PersonID = pop.PersonID
) AS e
WHERE id = 1
UNION ALL
SELECT e.id,
e.Name,
cte.Value + ',' + CAST(e.Value AS VARCHAR(MAX)) AS Value,
cte.ValueCount + 1 AS ValueCount
FROM (
SELECT ROW_NUMBER() OVER (PARTITION BY Name ORDER BY Name) AS id,
Name,
Value
FROM Person AS per
INNER JOIN Popularity AS pop
ON per.PersonID = pop.PersonID
) AS e
INNER JOIN cte
ON e.id = cte.id + 1
AND e.Name = cte.Name
)
SELECT p.Name, agg.Value
FROM Person p
LEFT JOIN (
SELECT Name, Value
FROM (
SELECT Name,
Value,
ROW_NUMBER() OVER (PARTITION BY Name ORDER BY ValueCount DESC)AS id
FROM cte
) AS p
WHERE id = 1
) AS agg
ON p.Name = agg.Name
This is an example result:
--------------------------------
| Name | Value |
--------------------------------
| John Smith | 1.2,5,4.2 |
--------------------------------
| John Doe | NULL |
--------------------------------
| Jane Smith | 8 |
--------------------------------
As per in Oracle you can use listagg to achive this -
select t1.Person_Name, listagg(t2.Popularity_Value)
within group(order by t2.Popularity_Value)
from t1, t2
where t1.Person_Name = t2.Person_Name (+)
group by t1.Person_Name
I hope this will solve your problem.
But the comment you have given after #DavidJashi question .. well this is not sql standard and I think he is correct. I am also with David that you can not achieve this in pure sql statement.
I know that I'm SUPER late to the party, but for anyone else that might find this, I don't believe that this is possible using pure SQL92. As I discovered in the last few months fighting with NetSuite to try to figure out what Oracle methods I can and cannot use with their ODBC driver, I discovered that they only "support and guarantee" SQL92 standard.
I discovered this, because I had a need to perform a LISTAGG(). Once I found out I was restricted to SQL92, I did some digging through the historical records, and LISTAGG() and recursive queries (common table expressions) are NOT supported in SQL92, at all.
LISTAGG() was added in Oracle SQL version 11g Release 2 (2009 – 11 years ago: reference https://oracle-base.com/articles/misc/string-aggregation-techniques#listagg) , CTEs were added to Oracle SQL in version 9.2 (2007 – 13 years ago: reference https://www.databasestar.com/sql-cte-with/).
VERY frustrating that it's completely impossible to accomplish this kind of effect in pure SQL92, so I had to solve the problem in my C# code after I pulled a ton of extra unnecessary data. Very frustrating.