Creating a column in runtime - sql-server

I have a Query
select Distinct EmailAddress as MailId from tblUsers
it shows the emails, but i want to create a column through the query which will show total number of emails.
Please help me

SELECT *, COUNT(1) OVER ()
FROM (
SELECT DISTINCT EmailAddress
FROM tblUsers
) t
Just for R&D -
CREATE FUNCTION dbo.udf_GetCount ()
RETURNS INT
WITH SCHEMABINDING
AS BEGIN
RETURN (SELECT COUNT(DISTINCT EmailAddress) FROM tblUsers)
END
GO
ALTER TABLE dbo.tblUsers
ADD TotalCnt AS dbo.udf_GetCount() PERSISTED
GO
or
SELECT DISTINCT EmailAddress, t.TotalCnt
FROM tblUsers
CROSS JOIN (
SELECT TotalCnt = COUNT(DISTINCT EmailAddress)
FROM tblUsers
) t

Query
CREATE TABLE #email
(
email VARCHAR(MAX)
);
INSERT INTO #email VALUES
('a.c#s'),
('a.c#s'),
('b.c#s'),
('b.c#s'),
('c.c#s'),
('c.c#s'),
('d.c#s');
If you want count of each email, then
SELECT email, COUNT(email) AS [Count]
FROM #email
GROUP BY email;
Result
+-------+-------+
| email | Count |
+-------+-------+
| a.c#s | 2 |
| b.c#s | 2 |
| c.c#s | 2 |
| d.c#s | 1 |
+-------+-------+
else count of total distinct email
SELECT DISTINCT email, (SELECT COUNT(DISTINCT email) FROM #email) AS [Count]
FROM #email;
Result
+-------+-------+
| email | Count |
+-------+-------+
| a.c#s | 4 |
| b.c#s | 4 |
| c.c#s | 4 |
| d.c#s | 4 |
+-------+-------+
SQL Fiddle demo

Related

How to append metadata to a grouped selection by primary key

I have a list of favorites like:
Sample Data
| key | item_id | list_name | customer_id |meta |
|-----|---------|-----------|--------------|---------------|
| 1 | A-11 | aa11 | 001 | unique-data-1 |
| 2 | A-11 | bb22 | 001 | unique-data-2 |
| 3 | A-26 | cc33 | 001 | unique-data-3 |
| 4 | A-28 | aa11 | 002 | unique-data-4 |
| 5 | J-52 | aa11 | 001 | unique-data-5 |
| 6 | X-53 | aa11 | 001 | unique-data-6 |
Desired Output
for #item_id nvarchar(20) = 'A-11'
| key | isFavorited | list_name | meta |
|-----|-------------|-----------|---------------|
| 1 | Y | aa11 | unique-data-1 |
| 2 | Y | bb22 | unique-data-2 |
| 3 | N | cc33 | unique-data-3 |
And would like to return a selection of all available lists, as well as whether or not a particular item is part of that list, with its meta data.
declare #item_id nvarchar(20) = 'A-11'
declare #customer_id nvarchar(20) = 001
select
[key],
[isFavorited] = max(case when [item_id] = #item_id then 'Y' else 'N' end)
[list_name]
[meta]
from favorites
where customer_id = #customer_id
group by [list_name], [key], [meta]
Issues when trying various methods:
The issue I'm having is that since the meta is unique the group by destroys the uniqueness of the select
A cross apply like the following doesn't apply the correct meta based on a matching key.
cross apply (
select top 1
[meta]
from favorites
where customer_id = #customer_id
)
When selecting by row number, the actual key to join back to is lost, so I'm unable to join the meta.
"noRow" = row_number() over(order by h_po_no asc)
I'd like to
Pass in an item_id and customer_id
Return all lists for that customer
Get favorite status for each list of passed in item_id
An item is flagged favorite if it matches both list_name and item_id for a given customer_id
Get row primary key and meta data
How can I return a distinct selection of list_name, isFavorite status, key, and it's meta?
To return the desired output you don't need any aggregation at all. A simple case expression and a where clause will accomplish this.
declare #Favorites table
(
MyKey int
, item_id varchar(10)
, list_name varchar(10)
, customer_id varchar(10)
, meta varchar(20)
)
insert #Favorites values
(1, 'A-1', 'list-1', '001', 'unique-data-1')
, (2, 'A-1', 'list-2', '001', 'unique-data-2')
, (3, 'A-2', 'list-3', '001', 'unique-data-3')
, (4, 'A-2', 'list-1', '002', 'unique-data-1')
select *
from #Favorites
declare #item_id nvarchar(20) = 'A-1'
, #customer_id nvarchar(20) = '001'
select f.MyKey
, isFavorited = case when f.item_id = #item_id then 'Y' else 'N' end
, listName = f.list_name
, f.meta
from favorites f
where f.customer_id = #customer_id
order by f.MyKey
Test Parameters
declare #item_id nvarchar(20) = 'A-11'
declare #customer_id nvarchar(20) = 001
Solution
drop table if exists #tmp
;with cte as (
select
[key],
[list_name],
[rn] = row_number() over (partition by list_name order by list_name desc)
from favorites
where customer_id = #customer_id
group by [list_name], [key], [meta]
)
select *
into #tmp
from cte
where [rn] = 1
select
[i],
[json],
#t.[tmp]
from #tmp #t
inner join (
select
[isFavorited] = max(case when [item_id] = #item_id then 'Y' else 'N' end)
[list_name]
from favorites
where customer_id = #customer_id
group by [list_name]
) j
on j.list_name = #t.list_name

SQL Server: Creating transposed table and joining with existing table

I have a set of data from table [MSPWIP].[MSPWIP].[Event] that looks like this:
| Createdby | StationName | SerialNumber |
-------------------------------------------------------
| Jay | L1.A1 | 22191321572 |
| Allan | L1.A2 | 22191321572 |
| Nathan | L2.A1 | 22191321579 |
| Jane | L2.A2 | 22191321579 |
And I have other sets of data that I have already joined in another query which is not relevant to the problem
I want to create a table separating the operator (denoted by createdby) by stations where L1.A1 means Line 1 Station 1 for example. For me at the moment, Line is not relevant
My ideal data after I restructure it should look like this
| SerialNumber | Operator1 | Operator2 |
----------------------------------------
| 22191321572 | Jay | Allan |
| 22191321579 | Nathan | Jane |
I tried using this code to Join both tables:
Query#1
Declare #Operator1 Table(
SerialNumber Varchar(255),
Operator1 Varchar(255)
)
Insert Into #Operator1 (Serialnumber, Operator1)
Select
SerialNumber,
Createdby as Operator1
From [MSPWIP].[MSPWIP].[Event]
where StationName like '%01'
Declare #Operator2 Table(
SerialNumber Varchar(255),
Operator2 Varchar(255)
)
Insert Into #Operator2 (Serialnumber, Operator2)
Select
SerialNumber,
CreatedBy as Operator2
From [MSPWIP].[MSPWIP].[Event]
where StationName like '%02'
select
a.SerialNumber,
CreatedBy,
b.Operator2
From #Operator1 a
join #Operator2 b
On a.SerialNumber = b.SerialNumber
Where a.SerialNumber In ('22191321572', '22191321574')
Then I would like to join it with that other query using the code below:
Query#2
join #Operator1 i
on a.SerialNumber = i.SerialNumber
join #Operator2 j
on a.SerialNumber = j.SerialNumber
Note that a is a different table.
However with Query#1 it only managed to show the headings and not the data, and this also caused Query#2 to also display heading and nothing else.
Just wondering if there was something wrong with Query#1 where the data failed to be inserted into the columns?
============================================
Update:
Using the answer below (with Modifications) I came up with a code like this
Query#3
SELECT Distinct*
FROM (
SELECT distinct
SerialNumber,
Case When t.StationName like '%A1' then CreatedBy End Operator1,
Case When t.StationName like '%A2' then CreatedBy End Operator2
--, Max(CASE WHEN CAST(RIGHT(t.StationName, 1) AS Varchar(255)) = 1 THEN t.CreatedBy END) Operator1
--, Max(CASE WHEN CAST(RIGHT(t.StationName, 1) AS Varchar(255)) = 2 THEN t.CreatedBy END) Operator2
FROM [MSPWIP].[MSPWIP].[Event] t
where t.CreatedDate > '2019-05-30'
Group BY SerialNumber, StationName, Createdby
) d
However my results now became staggered like so:
| SerialNumber | Operator1 | Operator2 |
----------------------------------------
| 22191321572 | Jay | NULL |
| 22191321572 | NULL | Allan |
| 22191321579 | Nathan | NULL |
| 22191321579 | NULL | Jane |
Did i do something wrong here?
You can save your time by doing it in one run like this :
SELECT *
FROM (
SELECT
SerialNumber
, MAX(CASE WHEN RIGHT(t.StationName, 2) = '01' THEN t.Operator END) Operator1
, MAX(CASE WHEN RIGHT(t.StationName, 2) = '02' THEN t.Operator END) Operator2
FROM [MSPWIP].[MSPWIP].[Event] t
GROUP BY SerialNumber
) d
then you just join it with the required tables.
P.S : If your station part in the StationName is not always a number, then you can use SUBSTRING(t.StationName, CHARINDEX('.', t.StationName) + 1, LEN(t.StationName)) instead of RIGHT(t.StationName, 2) to get the station part (which is after the dot).

How to generate XML path within CASE

Edit: Trying to query a XML Path list that has been narrowed down by a case statement. Column 'displayname' contains over 700 unique values throughout the database. However, based on other criteria including the AccountID and if RenderedValue is = '', the remaining results will most likely be less than 5. The variables in my query is I cannot explicitly declare an Account Id or DisplayName.
I have a successful CASE statement on it's own. But trying to also have the XML PATH statement pulls all the data from the table and comma separates it instead of just the results from the previous CASE statement. Can't figure out how to nest them together. Besides the GUID in column 1, values are nvarchar.
Query w/o CASE
select tb1.AccountID,
tb3.DisplayName,
tb4.RenderedValue
from Accounts tb1
join Display tb2 on tb2.AccountID = tb1.AccountID
inner join ExtractDetail tb3 on tb3.ExtractID = tb2.ExtractID
left join ExtractDetailData tb4 on tb4.ExtractDetailID = tb3.ExtractDetailID
result:
+-----------+---------------+-----------------------+
| AccountID | DisplayName | RenderedValue |
+-----------+---------------+-----------------------+
| E8175 | FirstName | John |
| E8175 | LastName | Smith |
| E8175 | StreetAddress | 123 Washington Street |
| E8175 | City | |
| E8175 | State | NY |
| E8175 | ZipCode | |
| E8175 | PhoneNumber | 555-555-5555 |
| E8175 | Email | JohnSmith#aol.com |
+-----------+---------------+-----------------------+
Query w/ CASE
select tb1.AccountID,
CASE When tb4.RenderedValue = ''
Then tb3.DisplayName
Else ''
End As MissingField
from Accounts tb1
join Display tb2 on tb2.AccountID = tb1.AccountID
inner join ExtractDetail tb3 on tb3.ExtractID = tb2.ExtractID
left join ExtractDetailData tb4 on tb4.ExtractDetailID = tb3.ExtractDetailID
Where tb4.RenderedValue =''
Result:
+-----------+--------------+
| AccountID | MissingField |
+-----------+--------------+
| E8175 | City |
| E8175 | ZipCode |
+-----------+--------------+
Expected Output:
+-----------+--------------+
| AccountID | MissingField |
+-----------+--------------+
| E8175 | City,ZipCode |
+-----------+--------------+
i think this code will help you
create table #temp (AccountID varchar(20),DisplayName varchar(20),RenderedValue varchar(255))
insert into #temp (AccountID,DisplayName,RenderedValue) values
('E8175','FirstName','John')
insert into #temp (AccountID,DisplayName,RenderedValue) values
('E8175','LastName','Smith')
insert into #temp (AccountID,DisplayName,RenderedValue) values
('E8175','StreetAddress','123 Washington Street')
insert into #temp (AccountID,DisplayName,RenderedValue) values ('E8175','City','')
insert into #temp (AccountID,DisplayName,RenderedValue) values
('E8175','State','NY')
insert into #temp (AccountID,DisplayName,RenderedValue) values
('E8175','ZipCode','')
insert into #temp (AccountID,DisplayName,RenderedValue) values
('E8175','PhoneNumber','555-555-5555 ')
insert into #temp (AccountID,DisplayName,RenderedValue) values
('E8175','Email','JohnSmith#aol.com')
SELECT distinct
P.AccountID,
STUFF
(
(
SELECT ',' + case when RenderedValue = '' Then DisplayName Else '' End
FROM #temp M
FOR XML PATH(''), type
).value('.', 'varchar(max)'), 1, 1, ''
) AS Temp
FROM
#temp P
Drop table #temp

MS SQL SERVER pivot table aggregation function

I have a question about the application of the aggregation function that used in pivot function.
The table OCCUPATIONS looks like this:
+-----------+------------+
| Name | Occupation |
+-----------+------------+
| Ashley | Professor |
| Samantha | Actor |
| Julia | Doctor |
| Britney | Professor |
| Maria | Professor |
| Meera | Professor |
| Priya | Doctor |
| Priyanka | Professor |
| Jennifer | Actor |
| Ketty | Actor |
| Belvet | Professor |
| Naomi | Professor |
| Jane | Singer |
| Jenny | Singer |
| Kristeen | Singer |
| Christeen | Singer |
| Eve | Actor |
| Aamina | Doctor |
+-----------+------------+
The first column is name and second is occupation.
Now I want to make a pivot table that each column is one kind of occupation and name is sorted alphabetically and print NULL when no more names for an occupation.
The output should looks like this:
+--------+-----------+-----------+----------+
| Doctor | Professor | Singer | Actor |
+--------+-----------+-----------+----------+
| Aamina | Ashley | Christeen | Eve |
| Julia | Belvet | Jane | Jennifer |
| Priya | Britney | Jenny | Ketty |
| NULL | Maria | Kristeen | Samantha |
| NULL | Meera | NULL | NULL |
| NULL | Naomi | NULL | NULL |
| NULL | Priyanka | NULL | NULL |
+--------+-----------+-----------+----------+
Here the first column is Doctor, second is Professor, third is Singer and fourth is Actor. The code to generate result is
select [Doctor],[Professor],[Singer],[Actor] from (select o.Name,
o.Occupation, row_number() over(partition by o.Occupation order by
o.Name) id from OCCUPATIONS o) as src
pivot
(max(src.Name)
for src.Occupation in ([Doctor],[Professor],[Singer],[Actor])
) as m
But when I replace the table generated from here:
(select o.Name, o.Occupation, row_number() over(partition by o.Occupation order by o.Name) id from OCCUPATIONS o) as src' to 'OCCUPATIONS'
the result is like this:
Priya Priyanka Kristeen Samantha
I understand why this happens, because we take a MAX() in each group. However, in the previous result, I also use a MAX() function to generate NULL when there's no more names coming, it doesn't return a max value as my expected, instead it return every name.
My question is why this happens?
Thank you!
Here could be the source of issue:
row_number() over(partition by o.Occupation order by
o.Name) id from OCCUPATIONS o
The Row_Number here you are using is PARTITION BY o.Occupation, so in your PIVOT, it will pivot the records by the occupation group, which means the id is repeating. If you get rid of the PARTITION BY and just keep the Order by part, it should work.
Try this approach:
find the occupations with more people associated
generate table with a sequence of numbers from 1 to the number of people calculated in the previous point
join the table generated in point 2. four times with the original table each time filtering on a different Occupation
This is the query:
declare #tmp table([Name] varchar(50),[Occupation] varchar(50))
insert into #tmp values
('Ashley','Professor') ,('Samantha','Actor') ,('Julia','Doctor') ,('Britney','Professor') ,('Maria','Professor') ,('Meera','Professor') ,('Priya','Doctor') ,('Priyanka','Professor') ,('Jennifer','Actor') ,('Ketty','Actor') ,('Belvet','Professor') ,('Naomi','Professor') ,('Jane','Singer') ,('Jenny','Singer') ,('Kristeen','Singer') ,('Christeen','Singer') ,('Eve','Actor') ,('Aamina','Doctor')
--this variable contains the occuation that has more Names (rows) in the table
--it will be the number of total rows in output table
declare #Occupation_with_max_rows varchar(50)
--populate #Occupation_with_max_rows variable
select top 1 #Occupation_with_max_rows=Occupation
from #tmp
group by Occupation
order by count(*) desc
--generate final results joining 4 times the original table with the sequence table
select D.Name as Doctor,P.Name as Professor,S.Name as Singer,A.Name as Actor
from
(select ROW_NUMBER() OVER (ORDER BY [Name]) as ord from #tmp where Occupation = #Occupation_with_max_rows) O
left join
(select ROW_NUMBER() OVER (ORDER BY [Name]) as ord, [Name] from #tmp where Occupation='Doctor') D on O.ord = D.ord
left join
(select ROW_NUMBER() OVER (ORDER BY [Name]) as ord, [Name] from #tmp where Occupation='Professor') P on O.ord = P.ord
left join
(select ROW_NUMBER() OVER (ORDER BY [Name]) as ord, [Name] from #tmp where Occupation='Singer') S on O.ord = S.ord
left join
(select ROW_NUMBER() OVER (ORDER BY [Name]) as ord, [Name] from #tmp where Occupation='Actor') A on O.ord = A.ord
Results:
Please find below code which works as expected :
select [Doctor],[Professor],[Singer],[Actor]
from
(
select row_number() over (partition by occupation order by name)[A],name,occupation
from occupations
)src
pivot
(
max(Name)
for occupation in ([Doctor],[Professor],[Singer],[Actor])
)piv;

SQL Server - How to check if two items have the same relations to another set of items?

I have table with Employees (tblEmployee):
| ID | Name |
| 1 | Smith |
| 2 | Black |
| 3 | Thompson |
And a table with Roles (tblRoles):
| ID | Name |
| 1 | Submitter |
| 2 | Receiver |
| 3 | Analyzer |
I have also a table with relations of Employees to their Roles with many to many relation type (tblEmployeeRoleRel):
| EmployeeID | RoleID |
| 1 | 1 |
| 1 | 2 |
| 2 | 1 |
| 2 | 2 |
| 2 | 3 |
| 3 | 3 |
I need to select ID, Name from tblEmployee that have exaclty the same set of roles from tblEmployeeRoleRel as has the Employee with ID = 1. How can I do it?
Use a where clause to limit the roles you're looking at to those of employeeID of 1 and use a having clause to make sure that the employee's role count matches that of employee1.
SELECT A.EmployeeID
FROM tblEmployeeRoleRel A
WHERE Exists (SELECT 1
FROM tblEmployeeRoleRel B
WHERE B.EmployeeID = 1
and B.RoleID = A.RoleID)
GROUP BY A.EmployeeID
HAVING count(A.RoleID) = (SELECT count(C.RoleID)
FROM tblEmployeeRoleRel C
WHERE EmployeeID = 1)
This assumes that employeeID and roleID are unique in tblEmployeeRoleRel otherwise we may have to distinct the roleID fields above.
Declare #EmployeeID int = 1 -- change this to whatever employee ID you like, or perhaps you'd pass an Employee ID to it in a stored procedure.
Select Distinct e.EmployeeID -- normally distinct would incur extra overhead, but in this case you only want the employee IDs. not using Distinct when an employee has multiple roles will give you multiple employee IDs.
from tblEmployeeRoleRel as E
where E.EmployeeID not in
(Select EmployeeID from tblEmployeeRoleRel where RoleID not in (Select RoleID from tblEmployeeRoleRel where Employee_ID = #EmployeeID))
and exists (Select EmployeeID from tblEmployeeRoleRel where EmployeeID = e.EmployeeID) -- removes any "null" matches.
and E.Employee_ID <> #Employee_ID -- this keeps the employee ID itself from matching.

Resources