SQL: Add counters in select - sql

I have a table which contains names:
Name
----
John Smith
John Smith
Sam Wood
George Wright
John Smith
Sam Wood
I want to create a select statement which shows this:
Name
'John Smith 1'
'John Smith 2'
'Sam Wood 1'
'George Wright 1'
'John Smith 3'
'Sam Wood 2'
In other words, I want to add separate counters to each name. Is there a way to do it without using cursors?

Use ROW_NUMBER():
SELECT Name, ROW_NUMBER() OVER(Partition BY Name ORDER BY Name) as [Rank]
FROM MyTable

Doing:
select name, count(*) as total from table group by name;
will get you something that looks like this:
name | total
-------------+------------
John Smith | 2
-------------+------------
Sam Wood | 2
-------------+------------
George Wright| 1
This isn't what you really wanted though - ROW_NUMBER(), as ck pointed out, is what you want, but not all databases support it - mysql doesn't, for example. If you're using MySQL, this might help:
ROW_NUMBER() in MySQL

Related

SQL count in a table

I'm looking to add some form of count function to my table, but am not quite sure how to do it. The table I have is:
First name
Surname
Tom
James
Mike
James
Tom
James
Mike
Hamilton
William
Morris
Mike
James
Mike
James
I would like it to have a count, of the full names that come up twice or more, like so:
First name
Surname
Count
Tom
James
1
Mike
James
1
Tom
James
2
Mike
Hamilton
1
William
Morris
1
Mike
James
2
Mike
James
3
What is the best way to go about this in SQL? Unfortunately I need the result as per the table above, rather than simply:
First name
Surname
Count
Tom
James
2
Mike
James
3
Mike
Hamilton
1
William
Morris
1
row_number function should work
select *,
row_number() over(partition by [first name],surname order by [first name]) as count
from table_name
I believe using group by on multiple columns would be a more appropriate approach to
select [first name], surname, COUNT(*) from Employee Group BY [first name], surname;
I believe using group by on multiple columns would be a more appropriate approach to
select [first name], surname, COUNT(*) from Employee Group BY [first name], surname;
Result:

Assign value to a new column for all rows associate with unique value in another column

I need to assign name to a new 'responsible' column for all rows associate with customer.
If part of the string in 'codes' consist 'manager', manager's name should be assigned to the 'responsible' column. If there is no 'manager' in the codes column, 'responsible' columns should be populated with the 'empl_name' associate with the row.
I assume case and group by should be used?
table looks like:
cust_name empl_name codes
john mike empl, office
liza nick manager_1, remote
john kate empl, remote
john mike empl, remote
liza mike empl, office
david kate empl, remote
john mike empl, remote
liza mike empl, office
david mike empl, remote
chris jennifer manager_2, office
output should be:
cust_name empl_name codes responsible
john mike empl, office mike
liza nick manager_1, remote nick
john kate empl, remote kate
john mike empl, remote mike
liza mike empl, office nick
david kate empl, remote kate
john mike empl, remote mike
liza mike empl, office nick
david mike empl, remote mike
chris jennifer manager_2, office jennifer
My code (googled everything):
SELECT
c.cust_name,
e.emp_name,
a.codes,
FROM Billing as b
--- Code Labels in 1 single row, separated by comma
OUTER APPLY (
SELECT STUFF((
(SELECT ', ' + y.CodeLabelName
FROM CodeToLabelBridge x
JOIN CodeLabel y
ON y.CodeLabelId = x.CodeLabelId
WHERE x.CodeId = b.billing_code_id
FOR XML PATH(''), TYPE).value('.', 'varchar(max)')),1,1,''
) AS codes
) AS a
--- JOINS
JOIN Client as c
ON (b.billing_cust_id = c.cust_id)
JOIN Employer as e
ON (b.billing_emp_id = e.emp_id)
JOIN Code as sc
ON (b.billing_code_id = sc.codes_id)
--- Table with Client and associate Manager
WITH cte AS (
SELECT * ,
row_number() OVER(PARTITION BY t.cust_name, t.empl_name ORDER BY t.cust_name desc) AS [rn]
FROM t
WHERE t.codes LIKE '%manager%'
)
Select cust_name, empl_name from cte WHERE [rn] = 1
Then I'm stuck. I thought to JOIN cte table and main table on 'cust_name' field, however having issues with that.
It sounds like you want to get who is 'ultimately' responsible for a customer, if the data has a row for each contact/rep the customer has, and showing the manager, if exists. This (assuming that your table is Tbl) would do that:
select
a.*,
Responsible=coalesce((select min(b.empl_name)
from Tbl b
where a.cust_name=b.cust_name
and b.codes like '%manager%'), a.empl_name)
from Tbl a
I used min() to avoid errors which may occur if the customer had more than one row with 'manager' in Codes.
Coalesce takes the current row's empl_name if there is no other record with manager; because the select subquery would return NULL.

SQL Server convert row values to columns

I have an SQL table like this
Name1 Name2 Department1 Department2 Location1 Location2
----------------------------------------------------------------------
Jhon Alex IT Marketing London Seattle
Mark Dan Sales R&D Paris Tokyo
How can I query these results in this format:
Name Department Location
---------------------------------------
Jhon IT London
Alex Marketing Seattle
Mark Sales Paris
Dan R&D Tokyo
Use cross apply
DEMO
select name,department,location
from t
cross apply
(
values(name1,department1,location1),(name2,department2,location2)
)cc (name, department,location)
OUTPUT:
name department location
Jhon IT London
Alex Marketing Seattle
Mark Sales Paris
Dan R&D T Tokyo
You could try to use SQL Server's UNPIVOT operator, but honestly a plain union query might even perform better:
SELECT Name1 AS Name, Department1 AS Department, Location1 AS Location FROM yourTable
UNION ALL
SELECT Name2, Department2, Location2 FROM yourTable;
Regarding your expected ordering, there is no sort of id column in your original table which maintains to which name pair each record belongs. So, what I have written above might be the best we can do here.
Try This:
DECLARE #TestDemo AS TABLE(Name1 VARCHAR(10),Name2 VARCHAR(10),Department1 VARCHAR(10),Department2 VARCHAR(10),Location1 VARCHAR(10),Location2 VARCHAR(10))
INSERT INTO #TestDemo VALUES('Jhon','Alex','IT','Marketing','London','Seattle')
INSERT INTO #TestDemo VALUES('Mark','Dan','Sales','R&D','Paris','Tokyo')
SELECT Name1 'Name',Department1 'Department',Location1 'Location' FROM #TestDemo
UNION ALL
SELECT Name2 'Name',Department2 'Department',Location2 'Location' FROM #TestDemo

pl/sql query to remove duplicates and replace the data

I have the following table:
data_id new_data_id first_name last_name
1 john smith
2 john smith
3 john smith
4 jeff louis
5 jeff louis
6 jeff louis
The above table has duplicate first and last names, and the data_id is different for all of them. In order to remove these duplicates, I would need to write a SQL query to replace the highest data_id in new_data_id column. My output would look something like this:
data_id new_data_id first_name last_name
1 3 john smith
2 3 john smith
3 3 john smith
4 6 jeff louis
5 6 jeff louis
6 6 jeff louis
How would I do this?
What you're looking for is an Oracle analytic function.
The aggregate function MAX can be used to select the highest data_id from your entire resultset, but that's not exactly what you need. Instead, use its alter ego, the MAX analytic function like so:
SELECT
data_id,
MAX(data_id) OVER (PARTITION BY first_name, last_name) AS new_data_id,
first_name,
last_name
FROM employees
ORDER BY data_id
This works by "partitioning" your resultset by first_name and last_name, and then it performs the given function within that subset.
Good luck!
Here's a fiddle: http://sqlfiddle.com/#!4/48b29/4
More info can be found here:
http://docs.oracle.com/cd/E11882_01/server.112/e41084/functions004.htm#SQLRF06174
If you need a change in place, a correlated update is probably the simplest way to write that:
UPDATE T
SET "new_data_id" =
(SELECT MAX("data_id") FROM T T2
WHERE T2."first_name" = T."first_name"
AND T2."last_name" = T."last_name")
See http://sqlfiddle.com/#!4/51a69/1

sql "group by" same PersonID, different PersonNames. Eliminate duplicates

I have a (rather dirty) datasource (excel) that looks like this:
ID | Name | Subject | Grade
123 | Smith, Joe R. | MATH | 2.0
123 | Smith, Joe Rodriguez | FRENCH | 3.0
234 | Doe, Mary Jane D.| BIOLOGY | 2.5
234 | Doe, Mary Jane Dawson| CHEMISTRY | 2.5
234 | Doe, Mary Jane | FRENCH | 3.5
My application's output should look like this:
Smith, Joe R.
123
MATH | 2.0
FRENCH | 3.0
So basically I want to do query (just for the ID/Person parent 'container') something like:
SELECT DISTINCT ID, Name FROM MyTable<br/>
or
SELECT ID, Name FROM MyTable GROUP BY ID
Of course both of the above are invalid and won't work.
I would like to 'combine' the same ID's and ignore/truncate the other records with the same ID/different Name (because we all know they're the same person since ID is our identifier and clearly it's just a typo/dirty data).
Can this be done by a single SELECT query?
If you don't really care which value shows up in the name field, use MAX() or MIN():
SELECT ID,
MAX(Name) AS Name
FROM [YourTable]
GROUP BY ID
Here's a working example to play with: https://data.stackexchange.com/stackoverflow/q/116699/
You can find the MIN or MAX Value of Name
SELECT ID, Max(Name)
FROM MyTable
GROUP BY ID
SELECT A.ID, A.NAME, T.Subject, T.Grade
FROM (SELECT ID, MIN(NAME) AS NAME
FROM MyTable
GROUP BY ID) A
LEFT JOIN MyTable T on A.ID = T.ID
Will give you something like
123 Smith, Joe R. Math 2.0
123 Smith, Joe R. FRENCH 3.0
234 Doe, Mary Jane BIOLOGY 2.5
234 Doe, Mary Jane CHEMISTRY 2.5
234 Doe, Mary Jane FRENCH 3.5
If you don't care which name you keep, you can use a MAX() or MIN() aggregate to pick just one name:
SELECT ID, MAX(Name) as Name
FROM MyTable GROUP BY ID