Arrange rows in T-SQL - sql-server

How to arrange rows manually in T-SQL?
I have a table result in order like this:
Unknown
Charlie
Dave
Lisa
Mary
but the expected result is supposed to be:
Charlie
Dave
Lisa
Mary
Unknown
edited:
My whole query is:
select (case when s.StudentID is null then 'Unknown' else s.StudentName end) as StudentName from Period pd full join Course c on pd.TimeID = c.TimeID full join Student s on c.StudentID = s.StudentID
group by s.StudentName, s.StudentID
order by case s.StudentName
when 'Charlie' then 1
when 'Dave' then 2
when 'Lisa' then 3
when 'Mary' then 4
when 'Unknown' then 5
end
but it didn't work. I think the problem root is because Unknown is from NULL value, as I wrote in that query that when StudentID is null then change "NULL" to "Unknown". Is this affecting the "stubborn" order of the result? By the way I also have tried order by s.StudentName asc but also didn't work.
Thank you.

Try the following...
SELECT os.StudentName
FROM ( SELECT CASE WHEN s.StudentID IS NULL THEN 'Unknown'
ELSE s.StudentName
END AS StudentName
FROM Period pd
FULL JOIN Course c ON pd.TimeID = c.TimeID
FULL JOIN Student s ON c.StudentID = s.StudentID
GROUP BY s.StudentName ,
s.StudentID
) AS os
ORDER BY os.StudentName
Edit: based on comment...
When I use this, it works fine...notice the Order By has no identifier
declare #tblStudent TABLE (StudentID int, StudentName varchar(30));
insert into #tblStudent values (null, '');
insert into #tblStudent values (1, 'Charlie');
insert into #tblStudent values (2, 'Dave');
insert into #tblStudent values (3, 'Lisa');
insert into #tblStudent values (4, 'Mary');
SELECT CASE WHEN s.StudentID IS NULL THEN 'Unknown'
ELSE s.StudentName
END AS StudentName
FROM #tblStudent s
GROUP BY s.StudentName ,
s.StudentID
ORDER BY StudentName

As I see your rows must be ordered alphabetically, so just add in the end of the query: ORDER BY p.StudentName.
If this not help, please add whole query, so we can find out the problem.
So when I see query I can explain. You try to sort by column p.StudentName. This column contains NULL. Try to sort by StudentName without p in front. This is alias of the expression which contains Unknown.

just put the following clause in you SQL statement:
order by p.StudentName
Sql server will order the column alphabetically.

Related

How to use self join to filter the records in same table in SQL Server 2012

I have a table which has both employees and team leaders.
I have below data
Declare #EMP AS TABLE(EmpID INT, EmpName VARCHAR(100),TeamLeadID INT)
INSERT INTO #EMP(EmpID, EmpName,TeamLeadID) VALUES
(1,'Ramesh',5)
,(2,'Mahesh',3)
,(3,'Vijay',3)
,(4,'Varma',5)
,(5,'Raj',5)
I want to list all employees along with thier teamleaderid and name. But the teamleaderid and name should be blank when the employeeid = teamleadid.
The result should be as follows.
EmpID EmpName TeamLeadID TeamLeadName
1 Ramesh 5 Raj
2 Mahesh 3 Vijay
3 Vijay NULL NULL
4 Varma 5 Raj
5 Raj NULL NULL
The code is as below.
SELECT E.EmpID,E.EmpName,TL.TeamLeadID,TL.EmpName AS TeamLeadName
FROM #Emp E
LEFT JOIN #Emp TL ON E.TeamLeadID = TL.EmpID AND E.EmpID ! = TL.EmpID
Is there any other way of rewriting this code with much performance (Mainly without using Not equal to at E.EmpID ! = TL.EmpID) and without using CTE?
If you want to avoid the NOT EQUAL in the WHERE clause, you can move your "logic" in the SELECT clause like this:
SELECT E.EmpID,E.EmpName,
IIF(TL.TeamLeadID=E.EmpID,NULL,TL.TeamLeadID) AS TeamLeadID ,
IIF(TL.EmpName=E.EmpName,NULL,TL.EmpName) AS TeamLeadName
FROM #Emp E
LEFT JOIN #Emp TL ON E.TeamLeadID = TL.EmpID
Whether it will be more performant is difficult to say. It depends on your data, and mostly affected records and number of teamleaders. Difference should be ridiculous if you don't have huge volumes
Another alternative is to use CASE WHEN clause in SELECT statement
SELECT E.EmpID,E.EmpName,
CASE
WHEN E.EmpID = TL.TeamLeadID
THEN NULL
ELSE TL.TeamLeadID
END AS TeamLeadID
,CASE
WHEN E.EmpID = TL.TeamLeadID
THEN NULL
ELSE TL.EmpName
END AS TeamLeadName
FROM #Emp E
LEFT JOIN #Emp TL ON E.TeamLeadID = TL.EmpID

SQL combine multiple records into one row

I have a table pulling userid's and their personal and work e-mails. I'd like to have one line per id showing both types of e-mails, but I can't figure out how to do that.
declare #t table(NPI int, email varchar(50), emailtype varchar(50))
insert into #t
values(1, 'john#home', 'personal'), (1, 'john#work', 'work');
This is the query I've written so far, which puts this on 2 separate rows:
select npi, case when emailtype = 'personal' then email end as personalemail,
case when emailtype = 'work' then email end as workemail
from #t;
Current Output:
npi personalemail workemail
1 john#home NULL
1 NULL john#work
What I'd like to see is:
npi personalemail workemail
1 john#home john#work
How can I do this?
This has been asked and answered around here about a million times. It is called conditional aggregation or crosstab. It is faster to write an answer than find one. As an alternative you could use PIVOT but I find the syntax a bit obtuse for me.
select NPI
, max(case when emailtype = 'personal' then email end) as PersonalEmail
, max(case when emailtype = 'work' then email end) as WorkEmail
from #t
group by NPI
Use pivot
SELECT
*
FROM #T
PIVOT
(
MAX(email)
FOR EmailType IN
(
personal,work
)
)Q

How to create a new "shared" group in a single SELECT query in MS SQL

I'm trying to group a SELECT like you'd normally do - AND at the same time make a new "shared/aggregate group" adding that to the original result-set without a secondary SELECT and UNION.
The secondary SELECT and UNION is out of the question since the real use of this is with some very big tables, with a lot of joins, so it would be waay to slow. So the UNION way is definitely out of the question.
I've tried my best to illustrate this with the following simplified example:
BEGIN TRAN
CREATE TABLE #MyTable
(
id INT,
name VARCHAR(255)
)
INSERT INTO #MyTable VALUES (1,'cola');
INSERT INTO #MyTable VALUES (2,'cola');
INSERT INTO #MyTable VALUES (3,'cola');
INSERT INTO #MyTable VALUES (4,'fanta');
INSERT INTO #MyTable VALUES (5,'fanta');
INSERT INTO #MyTable VALUES (6,'fanta');
INSERT INTO #MyTable VALUES (7,'water');
INSERT INTO #MyTable VALUES (8,'water');
INSERT INTO #MyTable VALUES (9,'water');
INSERT INTO #MyTable VALUES (10,'cola');
INSERT INTO #MyTable VALUES (11,'cola');
SELECT
CASE
WHEN name = 'cola' OR name = 'fanta'
THEN 'soda'
ELSE
name
END as name,
COUNT(distinct id) as count
FROM #MyTable
GROUP BY name
ROLLBACK TRAN
Actual output:
soda 5
soda 3
water 3
Desired output:
cola 5
fanta 3
soda 8 <- this is the "shared/aggregate group"
water 3
As Panagiotis Kanavos correctly pointed out in the comment above, this can be done using ROLLUP:
BEGIN TRAN
CREATE TABLE #BeverageType
(
name VARCHAR(255)
)
INSERT INTO #BeverageType VALUES ('Soda');
INSERT INTO #BeverageType VALUES ('Other');
CREATE TABLE #UserBeverage
(
id INT,
name VARCHAR(255)
)
INSERT INTO #UserBeverage VALUES (1,'cola');
INSERT INTO #UserBeverage VALUES (2,'cola');
INSERT INTO #UserBeverage VALUES (3,'cola');
INSERT INTO #UserBeverage VALUES (1,'fanta'); -- <- NOTE: user 1 drinks both cola and fanta so the as intended the user is only counted 1 time in the ROLLUP 'Soda' group (7)
INSERT INTO #UserBeverage VALUES (5,'fanta');
INSERT INTO #UserBeverage VALUES (6,'fanta');
INSERT INTO #UserBeverage VALUES (7,'water');
INSERT INTO #UserBeverage VALUES (8,'water');
INSERT INTO #UserBeverage VALUES (9,'water');
INSERT INTO #UserBeverage VALUES (10,'cola');
INSERT INTO #UserBeverage VALUES (11,'cola');
SELECT ub.name, bt.name AS groupName, COUNT(distinct id) as uniqueUserCount
FROM #UserBeverage as ub
JOIN #BeverageType as bt
ON CASE
WHEN (ub.name = 'water')
THEN 'Other'
ELSE
'Soda'
END = bt.name
GROUP BY ROLLUP(bt.name, ub.name)
ROLLBACK TRAN
Outputs:
cola Soda 5
fanta Soda 3
water Other 3
NULL Other 3
NULL Soda 7
NULL NULL 10
You should repeat CASE statement everywhere.
SELECT
CASE WHEN name = 'cola' OR name = 'fanta'
THEN 'soda' ELSE name END as name,
COUNT((CASE WHEN name = 'cola' OR name = 'fanta'
THEN 'soda' ELSE name END)) as count
FROM #MyTable
GROUP BY CASE WHEN name = 'cola' OR name = 'fanta'
THEN 'soda' ELSE name END
+-------+-------+
| name | count |
+-------+-------+
| soda | 8 |
+-------+-------+
| water | 3 |
+-------+-------+
Can I suggest to use a subquery:
SELECT name, count(*) AS count
FROM (SELECT CASE WHEN name = 'cola' OR name = 'fanta'
THEN 'soda' ELSE name END as name
FROM #MyTable) x
GROUP BY name;
If you need the aggregate as well as the individual products, then an alternative may be to use a UNION and select the aggregates as a second query.
SELECT name, count(distinct id) as count
FROM #MyTable
GROUP BY name
UNION
SELECT 'SODA', COUNT(distinct id) as count
FROM #MyTable
WHERE name = 'cola' or name ='fanta'
You might also use Søren Høyer Kristensen's summary table to get the aggregate names if you need more groupings.

How to check for a specific condition by looping through every record in SQL Server?

I do have following table
ID Name
1 Jagan Mohan Reddy868
2 Jagan Mohan Reddy869
3 Jagan Mohan Reddy
Name column size is VARCHAR(55).
Now for some other task we need to take only 10 varchar length i.e. VARCHAR(10).
My requirement is to check that after taking the only 10 bits length of Name column value for eg if i take Name value of ID 1 i.e. Jagan Mohan Reddy868 by SUBSTRING(Name, 0,11) if it equals with another row value. here in this case the final value of SUBSTRING(Jagan Mohan Reddy868, 0,11) is equal to Name value of ID 3 row whose Name is 'Jagan Mohan Reddy'. I need to make a list of those kind rows. Can somebody help me out on how can i achieve in SQL Server.
My main check is that the truncated values of my Name column should not match with any non truncated values of Name column. If so i need to get those records.
Assuming I understand the question, I think you are looking for something like this:
Create and populate sample data (Please save us this step in your future questions)
DECLARE #T as TABLE
(
Id int identity(1,1),
Name varchar(15)
)
INSERT INTO #T VALUES
('Hi, I am Zohar.'),
('Hi, I am Peled.'),
('Hi, I am Z'),
('I''m Zohar peled')
Use a cte with a self inner join to get the list of ids that match the first 10 chars:
;WITH cte as
(
SELECT T2.Id As Id1, T1.Id As Id2
FROM #T T1
INNER JOIN #T T2 ON LEFT(T1.Name, 10) = t2.Name AND T1.Id <> T2.Id
)
Select the records from the original table, inner joined with a union of the Id1 and Id2 from the cte:
SELECT T.Id, Name
FROM #T T
INNER JOIN
(
SELECT Id1 As Id
FROM CTE
UNION
SELECT Id2
FROM CTE
) U ON T.Id = U.Id
Results:
Id Name
----------- ---------------
1 Hi, I am Zohar.
3 Hi, I am Z
Try this
SELECT Id,Name
FROM(
SELECT *,ROW_NUMBER() OVER(PARTITION BY Name, LEFT(Name,11) ORDER BY ID) RN
FROM Tbale1 T
) Tmp
WHERE Tmp.RN = 1
loop over your column for all the values and put your substring() function inside this loop and I think in Sql index of string starts from 1 instead of 0. If you pass your string to charindex() like this
CHARINDEX('Y', 'Your String')
thus you will come to know whether it is starting from 0 or 1
and you can save your substring value as value of other column with length 10
I hope it will help you..
I think this should cover all the cases you are looking for.
-- Create Table
DECLARE #T as TABLE
(
Id int identity(1,1),
Name varchar(55)
)
-- Create Data
INSERT INTO #T VALUES
('Jagan Mohan Reddy868'),
('Jagan Mohan Reddy869'),
('Jagan Mohan Reddy'),
('Mohan Reddy'),
('Mohan Reddy123551'),
('Mohan R')
-- Get Matching Items
select *, SUBSTRING(name, 0, 11) as ShorterName
from #T
where SUBSTRING(name, 0, 11) in
(
-- get all shortnames with a count > 1
select SUBSTRING(name, 0, 11) as ShortName
from #T
group by SUBSTRING(name, 0, 11)
having COUNT(*) > 1
)
order by Name, LEN(Name)

Insert sum of rows and display it in the same table

I have a table as shown below
I want to insert the row below each employee having data as like same employeecode, employeename, project as 'bench', expected end & start dates as null , install capacity as sum of all install capacities of that employees-184 and allocation % as null.
Result should look like this.
Thanks in advance.
Try This:
INSERT INTO TABLE_NAME SELECT EMPLOYEEDODE, EMPLOYEENAME, 'BENCH' AS PROJECTNAME, NULL AS START_DT, NULL AS ENDDT, SUM(install_capacities), SUM(ActualPercentage) FROM TABLE_NAME GROUP BY EMPLOYEEDODE,EMPLOYEENAME;
Please try the below queries to produce the desired output:
insert into tbl(EmployeeCode, EmployeeName, projectName,ExpectedStartDate, ExpectedEndDate, InstallCapacity, [Actual Allocation Percentage])
select EmployeeCode,EmployeeName,'Bench' As projectName,NULL as ExpectedStartDate, NULL as ExpectedEndDate, SUM(InstallCapacity) as InstallCapacity, SUM([Actual Allocation Percentage]) as [Actual Allocation Percentage]
from tbl
group by EmployeeCode,EmployeeName
select * from tbl order by EmployeeCode,EmployeeName

Resources