Grouping based on pattern Sql - sql-server

Say I have the following table name t1(deptName,courseName,fund) I want to get sum of fund of each courseName with its deptName.So far all I can do is group by courseName and sum(fund) to get sum of fund under each courseName but how can I also include deptName on each output row like deptName,CourseName sum(fund)?
Thanks in advance.

One possibility: concatenate deptName and courseName and group by that (that literally fulfills what you are asking).
SELECT deptName + '_' + courseName as Dept_Course, SUM(fund)
FROM t1
GROUP BY (deptName + '_' + courseName)
Or (thanks #giorgi):
SELECT deptName + '_' + courseName as Dept_Course, SUM(fund)
FROM t1
GROUP BY deptName,courseName
Usually, you can just do this, though:
SELECT deptName,courseName,SUM(fund)
FROM t1
GROUP BY deptName,courseName

Related

Add Alphabet column in select statement

I want to add alphabet column when we get write a select query
my query is: -
select empname, address from Employee table
I want to get data like
empname address alphabetcolumn
Pramod USA A
xyz USA B
You can use below approach.
in ASCII table A starts from 65. ROW_NUMBER starts from 1. Hence, I am summing row_number with 64 to get 65+.
select
empname,
address,
CHAR((ROW_NUMBER() OVER(ORDER BY empname ASC)) + 64) AS ALPHABET
from Employee
with two characters support you can use this approach
With r as
(
select
empname,
address,
CHAR((ROW_NUMBER() OVER(ORDER BY empname ASC)) + 64) AS rn
from Employee
)
select IIF(rn / 26 = 0, '', char(rn/26 + 64)) + char((rn -1) % 26 +65)
from r;
I get the idea that this isn't what the OP is after, but based on the limited information.
You can add a new column to your table by using:
ALTER TABLE Employee ADD alphabetcolumn char(1);

Is ordering done by actual column or alias?

Let's say I have a simple query like this:
select
subgroup,
subgroup + ' (' + cast(grade as varchar(1)) + 'G)' as grade,
count(*) as 'count'
From table_empl
where year(EnterDate) = year(getdate())
group by subgroup, grade
order by grade
It seems that order by grade is being ordered by the alias grade instead of the actual column grade; at least that's what the result shows.
Is this correct?
Since I can't change the columns that are included in the result, is the solution to add an alias to the actual column? Something like this?
select
grade as 'grade2',
subgroup,
subgroup + ' (' + cast(grade as varchar(1)) + 'G)' as grade,
count(*) as 'count'
From table_empl
where year(EnterDate) = year(getdate())
group by subgroup,grade
order by grade2
If you prefix the column name by its table name (or an alias given to the table in the FROM clause) in the ORDER BY clause, then it will use the column, not the expression computed in the SELECT clause and given the same name as the column.
So this should sort using the original grade column:
select
subgroup,
subgroup + ' (' + cast(grade as varchar(1)) + 'G)' as grade,
count(*) as 'count'
From table_empl
where year(EnterDate) = year(getdate())
group by subgroup, grade
order by table_empl.grade
Or:
select
subgroup,
subgroup + ' (' + cast(grade as varchar(1)) + 'G)' as grade,
count(*) as 'count'
From table_empl t
where year(EnterDate) = year(getdate())
group by subgroup, grade
order by t.grade
Instruction Order By runs after all instructions, even Select. And in this case it's correct to take alias instead actual column.
The clauses are processed in the following order:
FROM
WHERE
GROUP BY
HAVING
SELECT
ORDER BY
You can use name(Alias) of table to specify table column
A very good question. Apparently, the official documentation does not provide a direct answer to it. However, one can imply the observed behaviour from the following fact: the difference between column alias and column is that the latter can be prefixed with its parent table name (alias), whereas the former cannot.
Since you didn't specify the table name in the ORDER BY clause, the column alias takes root.

Combining 2 coloumns into 1

Find the customer number, last name, and first name. Display the name as one column titled "Name"
SELECT A.CustomerNumber (A.LName B.FName) AS Name
FROM Customers as A
LEFT JOIN Customers as B
ON A.CustomerNumber=B.CustomerNumber;
What can I use to do this properly
Shouldn't it be as simple as this?
I cannot see any value in your attempt to join a row with the same row of the same table...
SELECT CustomerNumber
,LName + ' ' + FName AS Name
FROM Customers;
Attention: Make sure, that both name parts are not NULL, otherwise the whole concatenated string will be NULL...
You can use the CONCAT-function, like this:
SELECT A.CustomerNumber, CONCAT(A.LName, ' ', B.FName) AS Name
FROM Customers as A
LEFT JOIN Customers as B
ON A.CustomerNumber=B.CustomerNumber;

Adding number to text rows sql server

I have a columns named id and item and there are stored values like:
id item
1 value
2 value
3 value
etc. There are 192 rows. These values are in the system in different places and I need to find concrete value in database to change it to the name I need.
Is there some posibility to add number to rows, for example value_01, value_02 etc.
I know how to do it in C language, but have no idea how to do it in sql server.
Edited:
#lad2025
In system I have columns, that names are stored in database.
Names are same, for example:
In app Apple I have table name Apple
In app Storage I also have table name Apple
I need to change app Storage columns name Apple to different, but I dont know, which of databasa Apple values it is, so I want to add identifiers to string, to find the right one. So I need to update database values, to see them in system.
SQLFiddleDemo
DECLARE #pad INT = 3;
SELECT
[id],
[item] = [item] + '_' + RIGHT(REPLICATE('0', #pad) + CAST([id] AS NVARCHAR(10)), #pad)
FROM your_table;
This will produce result like:
value_001
value_010
value_192
EDIT:
After reading your comments it is not clear what you want to achieve, but check:
SqlFiddleDemo2
DECLARE #pad INT = 3;
;WITH cte AS
(
SELECT *,
[rn] = ROW_NUMBER() OVER (PARTITION BY item ORDER BY item)
FROM your_table
)
SELECT
[id],
[item] = [item] + '_' + RIGHT(REPLICATE('0', #pad) + CAST([rn] AS NVARCHAR(10)), #pad)
FROM cte
WHERE item = 'value'; /* You can comment it if needed */

Group by custom function in T-SQL

I have a table of people in which there may be duplicates. My goal is to return a list of possible duplicates so that we can combine them into a new person.
I want to group by first_name and last_name, obviously. However, if both person records have a defined birth_date and those dates differ, then I want to exclude the records, since odds are the people are different but happen to have the same name.
The other caveat is that in our system (which I inherited), the birth_date column is NOT NULL, and non-specified birth_dates are set to '1900-01-01'.
Is there a way I can GROUP BY a custom function (or use some other clever logic) that either compares just the birth_date columns checking to see if both are not the default date or they are if the same, or else takes in arguments, like say each person_id and compares the records against each other, returning a BIT to decide whether they should count as the same group?
I'd like to avoid CLR-defined aggregate functions (since I'm inexperienced with it).
So far (without the birth_date comparison) the query I have is:
SELECT *
FROM core_person P
WHERE last_name + ',' + first_name IN
(SELECT last_name + ',' + first_name "name"
FROM core_person
GROUP BY last_name + ',' + first_name
HAVING COUNT(*) > 1)
ORDER BY last_name + ',' + first_name
I would like to add something to the GROUP BY clause to compare the birth dates.
You can use the nullif function to return nulls if the date is equal to 1/1/1900 as nullif(Birthday,'1/1/1900') to your advantage.
This query can get you started to see all the records with their possible matches:
select p1.person_id
from core_person p1
join core_person p2
on p1.person_id <> p2.person_id
and LEFT(p1.first_name,5) = LEFT(p2.first_name,5)
and LEFT(p1.last_name,5) = LEFT(p2.last_name,5)
and isnull(nullif(p1.Birthday,'1/1/1900'), p2.Birthday) = isnull(nullif(p2.Birthday,'1/1/1900'), p1.Birthday)
group by p1.person_id
If either one of the Birthday's are equal to 1/1/1900 it will compare the birthday to itself, otherwise it will only join on equality of the birthday's in both records.
If you don't want to see your matches, you can use a variation of the query above as a sub-query to return only id values that are duplicates:
select core_person
from core_person
where person_id in
(
select p1.person_id
from core_person p1
join core_person p2
on p1.person_id <> p2.person_id
and LEFT(p1.first_name,5) = LEFT(p2.first_name,5)
and LEFT(p1.last_name,5) = LEFT(p2.last_name,5)
and isnull(nullif(p1.Birthday,'1/1/1900'), p2.Birthday) = isnull(nullif(p2.Birthday,'1/1/1900'), p1.Birthday)
group by p1.person_id
)
Instead of grouping, will something like this work for you?
select * from MyTable a
left join MyTable b
on a.person_id < b.person_id
and a.first_name = b.first_name
and a.last_name = b.last_name
and (
a.birthdate = b.birthdate
or a.birthdate = '1900-1-1'
or b.birthdate = '1900-1-1'
)
It matches rows where last name and first name match, and either birthdates match or one birthdate is your placeholder value. The person_ID part of the join gets rid of duplicates (e.g. 1 matches to 2, then another row where 2 matches to 1, or 1 matches to 1).
You may want to broaden the match criteria for the names to look at first few characters or use SOUNDEX, but then your matches would probably require more hand-sorting as a final step.
Edit: to return a list of all records that have a possible duplicate in the table, not associated with their matches, use this instead:
select distinct a.* from MyTable a
inner join MyTable b
on a.person_id <> b.person_id
and a.first_name = b.first_name
and a.last_name = b.last_name
and (
a.birthdate = b.birthdate
or a.birthdate = '1900-1-1'
or b.birthdate = '1900-1-1'
)
order by a.first_name, a.last_name, a.birthdate

Resources