Display a blank row between every unique row? - sql

I have a simple query like:
SELECT employee, ITEM_TYPE, COUNT(ITEM_TYPE)
FROM hr_database
So the output may look like
BOB MUGS 4
BOB PENCILS 10
CAT MUGS 2
CAT PAPERCLIPS 7
SAL MUGS 11
But for readability, I want to put a blank row between each user in the output(i.e for readability), like this :
BOB MUGS 4
BOB PENCILS 10
CAT MUGS 2
CAT PAPERCLIPS 7
SAL MUGS 11
Is there a way to do this in Oracle SQL ? So far, I found this link but it doesn't match what I need . I'm thinking to use a WITH in the query?

You can do it in the database, but this type of processing should really be done at the application layer.
But, it is kind of an amusing trick to figure out how to do it in the database, and that is your specific question:
WITH e AS (
SELECT employee, ITEM_TYPE, COUNT(ITEM_TYPE) as cnt
FROM hr_database
GROUP BY employee, ITEM_TYPE
)
SELECT (case when cnt is not null then employee end) as employee,
item_type, cnt
FROM (select employee, item_type, cnt, 1 as x from e union all
select distinct employee, NULL, NULL, 2 as x from e
) e
ORDER BY e.employee, x;
I emphasize, though, that this is really for amusement and perhaps for understanding better how SQL works. In the real world, you do this type of work at the application layer.
A summary of how this works. The union all brings in one additional row for each employee. The x is a priority for sorting -- because you have to sort the result set to get the proper ordering. The case statement is needed to prevent the employee from being in the first column. cnt should never be NULL for the valid rows.

You can try like this with normal union & distinct
select emp,item_type,cnt from
(select distinct ' ' as emp,' ' as item_type ,' ' as cnt, employee
from hr_database
union
select employee as emp,item_type ,to_char(count(item_type)) as cnt, employee
from hr_database
group by employee,item_type)a
order by a.employee

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 to get dates based on months that appear more than once?

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;

Case Statement for multiple criteria

I would like to ignore some of the results of my query as for all intents and purposes, some of the results are a duplicate, but based on the way the request was made, we need to use this hierarchy and although we are seeing different 'Company_Name' 's, we need to ignore one of the results.
Query:
SELECT
COUNT(DISTINCT A12.Company_name) AS Customer_Name_Count,
Company_Name,
SUM(Total_Sales) AS Total_Sales
FROM
some_table AS A12
GROUP BY
2
ORDER BY
3 ASC, 2 ASC
This code omits half a doze joins and where statements that are not germane to this question.
Results:
Customer_Name_Count Company_Name Total_Sales
-------------------------------------------------------------
1 3 Blockbuster 1,000
2 6 Jimmy's Bar 1,500
3 6 Jimmy's Restaurant 1,500
4 9 Impala Hotel 2,000
5 12 Sports Drink 2,500
In the above set, we can see that numbers 2 & 3 have the same count and the same total_sales number and similar company names. Is there a way to create a case statement that takes these 3 factors into consideration and then drops one or the other for Jimmy's enterprises? The other issue is that this has to be variable as there are other instances where this happens. And I would only want this to happen if the count and sales number match each other with a similar name in the company name.
Desired result:
Customer_Name_Count Company_Name Total_Sales
--------------------------------------------------------------
1 3 Blockbuster 1,000
2 6 Jimmy's Bar 1,500
3 9 Impala Hotel 2,000
4 12 Sports Drink 2,500
Looks like other answers are accurate based on assumption that Company_IDs are the same for both.
If Company_IDs are different for both Jimmy's Bar and Jimmy's Restaurant then you can use something like this. I suggest you get functional users involved and do some data clean-up else you'll be maintaining this every time this issue arise:
SELECT
COUNT(DISTINCT CASE
WHEN A12.Company_Name = 'Name2' THEN 'Name1'
ELSE A12.Company_Name
END) AS Customer_Name_Count
,CASE
WHEN A12.Company_Name = 'Name2' THEN 'Name1'
ELSE A12.Company_Name
END AS Company_Name
,SUM(A12.Total_Sales) AS Total_Sales
FROM some_table er
GROUP BY CASE
WHEN A12.Company_Name = 'Name2' THEN 'Name1'
ELSE A12.Company_Name
END
Your problem is that the joins you are using are multiplying the number of rows. Somewhere along the way, multiple names are associated with exactly the same entity (which is why the numbers are the same). You can fix this by aggregating by the right id:
SELECT COUNT(DISTINCT A12.Company_name) AS Customer_Name_Count,
MAX(Company_Name) as Company_Name,
SUM(Total_Sales) AS Total_Sales
FROM some_table AS A12
GROUP BY Company_id -- I'm guessing the column is something like this
ORDER BY 3 ASC, 2 ASC;
This might actually overstate the sales (I don't know). Better would be fixing the join so it only returned one name. One possibility is that it is a type-2 dimension, meaning that there is a time component for values that change over time. You may need to restrict the join to a single time period.
You need to have function to return a common name for the companies and then use DISTINCT:
SELECT DISTINCT
Customer_Name_Count,
dbo.GetCommonName(Company_Name) as Company_Name,
Total_Sales
FROM dbo.theTable
You can try to use ROW_NUMBER with window function to make row number by Customer_Name_Count and Total_Sales then get rn = 1
SELECT * FROM (
SELECT *,ROW_NUMBER() OVER(PARTITION BY Customer_Name_Count,Total_Sales ORDER BY Company_Name) rn
FROM (
SELECT
COUNT(DISTINCT A12.Company_name) AS Customer_Name_Count,
Company_Name,
SUM(Total_Sales) AS Total_Sales
FROM
some_table AS A12
GROUP BY
Company_Name
)t1
)t1
WHERE rn = 1

Complex SQL query or queries

I looked at other examples, but I don't know enough about SQL to adapt it to my needs. I have a table that looks like this:
ID Month NAME COUNT First LAST TOTAL
------------------------------------------------------
1 JAN2013 fred 4
2 MAR2013 fred 5
3 APR2014 fred 1
4 JAN2013 Tom 6
5 MAR2014 Tom 1
6 APR2014 Tom 1
This could be in separate queries, but I need 'First' to equal the first month that a particular name is used, so every row with fred would have JAN2013 in the first field for example. I need the 'Last" column to equal the month of the last record of each name, and finally I need the 'total' column to be the sum of all the counts for each name, so in each row that had fred the total would be 10 in this sample data. This is over my head. Can one of you assist?
This is crude but should do the trick. I renamed your fields a bit because you are using a bunch of "RESERVED" sql words and that is bad form.
;WITH cte as
(
Select
[NAME]
,[nmCOUNT]
,ROW_NUMBER() over (partition by NAME order by txtMONTH ASC) as 'FirstMonth'
,ROW_NUMBER() over (partition by NAME order by txtMONTH DESC) as 'LastMonth'
,SUM([nmCOUNT]) as 'TotNameCount'
From Table
Group by NAME, [nmCOUNT]
)
,cteFirst as
(
Select
NAME
,[nmCOUNT]
,[TotNameCount]
,[txtMONTH] as 'ansFirst'
From cte
Where FirstMonth = 1
)
,cteLast as
(
Select
NAME
,[txtMONTH] as 'ansLast'
From cte
Where LastMonth = 1
Select c.NAME, c.nmCount, c.ansFirst, l.ansLast, c.TotNameCount
From cteFirst c
LEFT JOIN cteLast l on c.NAME = l.NAME

Count number of records with specific values

I have a table:
Table Teams
Id_team member_1 member_2 member_3
1 Alice Ben
2 Ben
3 Charles Alice Ben
4 Ben Alice
I will need to know in how many different teams Alice is a member (doesn't count if she is the first member, second or third). In my sample, the right answer is 2 (with Ben in Id_team 1 and 4, with Ben and Charles in Id_team = 3). Thank you!
You have to count "alices" in each column separately to ensure distinct oer column
What you appear to checking is "
SELECT
COUNT(DISTINCT CASE WHEN member_1 = 'Alice' THEN member_1 END) +
COUNT(DISTINCT CASE WHEN member_2 = 'Alice' THEN member_2 END) +
COUNT(DISTINCT CASE WHEN member_3 = 'Alice' THEN member_3 END)
FROM tablename
WHERE 'Alice' IN(member_1, member_2, member_3);
Update: fixed COUNT
Okay, so you want to same teams with different positions (e.g. Alice&Ben, Ben&Alice) count as one.
To do this, order the members in ascending order for alice in every position, and count the results (this returns 2 to your example):
SELECT COUNT(*) FROM
(
SELECT
least( member_2, member_3) AS l,
greatest(member_2, member_3) AS g
FROM teams
WHERE
member_1 = 'Alice'
UNION
SELECT
least( member_1, member_3) AS l,
greatest(member_1, member_3) AS g
FROM teams
WHERE
member_2 = 'Alice'
UNION
SELECT
least( member_1, member_2) AS l,
greatest(member_1, member_2) AS g
FROM teams
WHERE
member_3 = 'Alice'
) q
;
Note that this can only be done to the special case of 3 member teams, because least and greatest can select the two other members - for member coun of 4 and greater, a more complex solution is needed.
You can try to concatenate the fields (sorted alphabetically) in order to turn them into a list of strings.
Then run a distinct on this list (so it will list all separate teams)
Then search how many strings contains Alice
From this the hardest is the "concat alphabetically", as I couldn't really find any good function to do it, but a GROUP_CONCAT with a separate SELECTs and UNIONs to convert the fields into rows should do it:
SELECT COUNT(*)
FROM (
SELECT DISTINCT team_as_string
FROM (
SELECT id tid, GROUP_CONCAT(q ORDER BY q ASC SEPARATOR ',') team_as_string
FROM (
SELECT id, member_1 q FROM teams
UNION SELECT id, member_2 q FROM teams
UNION SELECT id, member_3 q FROM teams
/* add more fields if needed */
) c
GROUP BY tid
) b
) a
WHERE team_as_string LIKE '%Alice%'
I haven't checked it for syntax errors, but it should be fine logically. Tested and gives the correct answer (2)
This can be enhanced for more members, if needed.
Of course if the members are in a separate join table, then the whole group_concat part can be simplified.
I will need to know in how many different teams Alice is a member
Try this:
SELECT 'Alice', COUNT(id_team)
FROM tablename
WHERE 'Alice' IN(member_1, member_2, member_3);
The result:
| ALICE | THECOUNT |
--------------------
| Alice | 3 |
Fiddle Demo.
If id_team is not unique, use COUNT(DISTINCT id_team).