Column-values to rows-header without summarzie - sql-server

I have this simple single table:
Group Name
------------------------------
Group A Marco
Group B Sven
Group A Adrian
Group B Tina
Group B Steffi
Group C Jil
Group C Bastian
and want to rotate is this way. For every value in column Group one new column:
Group A Group B Group C
------------------------------
Adrian Steffi Bastian
Marco Sven Jil
Tina
Column "Group A", "Group B" and "Group C" are alphabetically sorted.
I tried it by "case when end" but the result was this:
Group A Group B Group C
------------------------------
Marco null null
Adrian null null
null Steffi null
null Sven null
null Tina null
null null Jil
null null Bastian
And it isn't sorted, either.
How can I achieve this? I need your help.

the static solution here : (but be carefully, in CTE a must be group having max count of names :))
(if you want sometheng more dynamically, write...)
;WITH CTE AS (SELECT ROW_NUMBER() OVER (partition by [Group] ORDER BY [Group]) AS RN,[Group], Name
FROM YourTable)
SELECT x.Name AS GroupA, a.Name AS GroupB, z.Name AS GroupC
FROM CTE a
OUTER APPLY(SELECT Name FROM CTE b WHERE a.RN = b.RN AND b.[Group] = 'Group A') x
OUTER APPLY(SELECT Name FROM CTE c WHERE a.RN = c.RN AND c.[Group] = 'Group C') z
WHERE a.[Group] = 'Group B'
Okay you can also use this one : edited by your request( but its only for 3 groups...)
DECLARE #param_group1 varchar(50),
#param_group2 varchar(50),
#param_group3 varchar(50)
SET #param_group1 =
(SELECT TOP 1 [Group]
FROM YourTable
GROUP BY [Group]
ORDER BY COUNT([Group]) DESC)
SET #param_group2 =
(SELECT TOP 1 [Group]
FROM YourTable
WHERE [Group] NOT IN (#param_group1)
GROUP BY [Group]
ORDER BY COUNT([Group]) DESC)
SET #param_group3 =
(SELECT TOP 1 [Group]
FROM YourTable
WHERE [Group] NOT IN (#param_group1, #param_group2)
GROUP BY [Group]
ORDER BY COUNT([Group]) DESC)
DECLARE #cmd VARCHAR(MAX)
SET #cmd = '
WITH CTE AS (SELECT ROW_NUMBER() OVER (partition by [Group] ORDER BY [Group]) AS RN,[Group], Name
FROM YourTable)
SELECT x.Name AS ['+#param_group1+'], a.Name AS ['+#param_group2+'], z.Name AS ['+#param_group3+']
into ##tmp1
FROM CTE a
OUTER APPLY(SELECT Name FROM CTE b WHERE a.RN = b.RN AND b.[Group] = '''+#param_group2+''') x
OUTER APPLY(SELECT Name FROM CTE c WHERE a.RN = c.RN AND c.[Group] = '''+#param_group3+''') z
WHERE a.[Group] = '''+#param_group1+''''
IF OBJECT_ID('tempdb..##tmp1') IS NOT NULL DROP TABLE ##tmp1
EXEC (#cmd)
SELECT [Group A],[Group B],[Group C]
FROM ##tmp1

Related

Get Max value for each User in a specific period SQL

I have the following query:
I am trying to select for each ACCOUNT_ID in the Table PAYMENT its maximum AMOUNT value.
It gives me a result but I suspect I am missing ACCOUNT_ID rows (I should get more ACCOUNT_ID)
SELECT a.REQUEST_DATE as PartyID, a.AMOUNT
FROM admin_all.PAYMENT a
LEFT OUTER JOIN admin_all.PAYMENT b
ON a.ACCOUNT_ID = b.ACCOUNT_ID AND a.AMOUNT < b.AMOUNT
WHERE b.ACCOUNT_ID IS NULL and a.TYPE='DEPOSIT' and a.STATUS='COMPLETED'
and (a.REQUEST_DATE between '2019-12-01' and '2019-12-17')
Can anybody indicate if my select is correct?
Maybe something like this
SELECT a.ACCOUNT_ID, MAX(a.AMOUNT)
FROM admin_all.PAYMENT a
WHERE a.TYPE='DEPOSIT' and a.STATUS='COMPLETED'
and (a.REQUEST_DATE between '2019-12-01' and '2019-12-17')
GROUP BY a.ACCOUNT_ID
--You can USE ROW_NUMBER() with OVER and PARTITION BY to break up the records
--into numbered rows ordered by the highest AMOUNT (DESC) and partitioned by the ID
--Then grab each record with a ROWNUM = 1
SELECT x.[PartyID], x.[AMOUNT]
FROM
(
SELECT a.REQUEST_DATE as PartyID, a.AMOUNT, ROW_NUMBER() OVER (PARTITION BY a.ACCOUNT_ID ORDER BY a.AMOUNT DESC) AS [RowNum]
FROM admin_all.PAYMENT a
LEFT OUTER JOIN admin_all.PAYMENT b
ON a.ACCOUNT_ID = b.ACCOUNT_ID AND a.AMOUNT < b.AMOUNT
WHERE b.ACCOUNT_ID IS NULL and a.TYPE='DEPOSIT' and a.STATUS='COMPLETED'
and (a.REQUEST_DATE between '2019-12-01' and '2019-12-17')
) AS x
WHERE x.[RowNum] = 1

SQL Simple Join with two tables, but one is random

I am stuck with this. I have a simple set-up with two tables. One table is holding emailaddresses one table is holding vouchercodes. I want to join them in a third table, so that each emailaddress has one random vouchercode.
Unfortunatly I am stuck with this as there are no identic Ids to match both values. What I have so far brings no result:
Select
A.Email
B.CouponCode
FROM Emailaddresses as A
JOIN CouponCodes as B
on A.Email = B.CouponCode
A hint would be great as search did not bring me any further yet.
Edit -
Table A (Addresses)
-------------------
Column A | Column B
-------------------------
email1#gmail.com True
email2#gmail.com
email3#gmail.com True
email4#gmail.com
Table B (Voucher)
-------------------
ABCD1234
ABCD5678
ABCD9876
ABCD5432
Table C
-------------------------
column A | column B
-------------------------
email1#gmail.com ABCD1234
email2#gmail.com ABCD5678
email3#gmail.com ABCD9876
email4#gmail.com ABCD5432
Sample Data:
While joining without proper keys is not a good solution, for your case you can try this. (note: not tested, just a quick suggestion)
;with cte_email as (
select row_number() over (order by Email) as rownum, Email
from Emailaddresses
)
;with cte_coupon as (
select row_number() over (order by CouponCode) as rownum, CouponCode
from CouponCodes
)
select a.Email,b.CouponCode
from cte_email a
join cte_coupon b
on a.rownum = b.rownum
You want to randomly join records, one email with one coupon each. So create random row numbers and join on these:
select
e.email,
c.couponcode
from (select t.*, row_number() over (order by newid()) as rn from emailaddresses t) e
join (select t.*, row_number() over (order by newid()) as rn from CouponCodes t) c
on c.rn = e.rn;
Give a row number for both the tables and join it with row number.
Query
;with cte as(
select [rn] = row_number() over(
order by [Column_A]
), *
from [Table_A]
),
cte2 as(
select [rn] = row_number() over(
order by [Column_A]
), *
from [Table_B]
)
select t1.[Column_A] as [Email_Id], t2.[Column_A] as [Coupon]
from cte t1
join cte2 t2
on t1.rn = t2.rn;
Find a demo here

Generate a new Id column in SQL Server that will be dependent on other 2 id columns

I want to generate a new id column as IndexID that will depend on 2 other ids. in my case the 2 other ids are OrderID and PatientOrderID.
This is my current table structure:
In this above image you can see there are 3 rows:
1st row has OrderID of 121336 and PatientOrderID of 230216
2nd row has OrderID of 121337 and PatientOrderID of 230217
3rd row has OrderID of 121337 and PatientOrderID of 230218
I want to add IndexId that will depend on OrderID and PatientOrderID.
For example:
1st row IndexID should be 1
2nd row IndexID should be 1
3rd row IndexId should be 2
This is because for each OrderID the IndexId will start at 1 & if there are 2 rows with same OrderID then we will check PatientOrderID to create the IndexID as 1,2 and so on.
Currently I have used dense_rank which is not correct in my query so please ignore it.
I am I will be able to make you understand my requirements. Any help would be appreciated. Thanks in advance.
Here is my query :
SELECT
O.ID AS OrderID,
smsFDGPatientOrder.ID AS PatientOrderID,
dense_rank() over (order by smsFDGPatientOrder.ID ) as IndexID,
--'Example' AS IndexID,
smsFDGPatientOrder.Isotope,
smsFDGPatientOrder.ActualIsotope,
OS.StatusID StatusID,
NCS.StatusID AS NoChangeStatusID,
smsFDGPatientOrder.[Order] AS FDGOrderID,
smsFDGPatientOrder.ExamDate,
smsFDGPatientOrder.ActualExamDate,
smsFDGPatientOrder.Indication,
smsFDGPatientOrder.[Procedure],
smsFDGPatientOrder.ActualProcedure
FROM
dbo.smsFDGOrder O WITH(NOLOCK)
INNER JOIN
[dbo].[smsFDGPatientOrder] as smsFDGPatientOrder WITH(NOLOCK) ON smsFDGPatientOrder.[Order] = O.[ID]
CROSS APPLY
(SELECT TOP 1 OS.StatusID
FROM dbo.smsFDGOrderStatus OS WITH(NOLOCK)
WHERE OS.FDGOrder = O.ID
ORDER BY OS.TimeAdded DESC, OS.ID DESC) OS
OUTER APPLY
(SELECT TOP 1 OS.StatusID
FROM dbo.smsFDGOrderStatus OS WITH(NOLOCK)
WHERE OS.FDGOrder = O.ID AND OS.StatusID != 3
ORDER BY OS.TimeAdded DESC, OS.ID DESC) NCS
INNER JOIN
dbo.smsStatus S WITH(NOLOCK) ON S.ID = OS.StatusID
LEFT JOIN
dbo.smsStatus NOChange WITH(NOLOCK) ON NOChange.ID = NCS.StatusID
WHERE
(S.Status IN ('In Queue'))
AND (S.Status != 'Changed' OR NOChange.Status IN ('In Queue'))
ORDER BY
O.ID
row_number () over (partition by O.Id order by smsFDGPatientOrder.Id) as IndexId
If I have your requirements right, you are right to use dense_rank. You just need to add a little extra logic:
cast(dense_rank() over (order by OrderID) as nvarchar(10))
+ case when dense_rank() over (order by PatientOrderID) > 1
then N',' + cast(dense_rank() over (partition by OrderID order by PatientOrderID) as nvarchar(10))
else N''
end as IndexID
Which will output 1 for the first OrderID and then 1,2, 1,3, etc for any additional PatientOrderID values in the same OrderID:
OrderID PatientOrderID IndexID
121336 230216 1
121337 230217 2,1
121337 230218 2,2

SQL: How can I get from grouped table also one first row from each group

I have following table
CREATE TABLE [dbo].[Table_1](
[ID] [int] IDENTITY(1,1) NOT NULL,
[Name] [nchar](10) NULL,
[DateOf] [nvarchar](20) NULL
) ON [PRIMARY]
and date as:
ID|Name|DateOf
1|A|2016-11-29 00:01:00
2|A|2016-11-29 00:02:00
3|A|2016-11-29 00:03:00
4|B|2016-11-29 00:01:00
5|B|2016-11-29 00:02:00
If I make like
select name, COUNT(name) from Table_1 group by Name
I'll have
A|3
B|2
So, how to get result like before with only one row taken from each group and sorted?
A|3|2016-11-29 00:01:00
B|2|2016-11-29 00:01:00
where last column will be sorted as date time (right now is nvarchar)
You can use a CTE and the ranking function PARTITION BY
WITH CTE AS
( select name, dateof,
rn = row_number() over (partition by NAME order by dateof desc)
from Table_1
)
SELECT name, dateof FROM CTE WHERE RN = 1
Or
select * from (
select name, dateof, ROW_NUMBER() over(partition by NAME order by dateof desc) as rnk
from Table_1
) a where rnk=1
You can use CROSS APPLY:
select t1.name, t1.[Count], t2.MinDate
from (
select name, count(*) as [Count]
from Table_1
group by name
) AS t1
cross apply (
select min(DateOf) as MinDate
from Table_1 t2
where t1.Name = t2.Name
) as t2
If you want to get additional data than just the min date from the row, you can modify the subselect:
select t1.name, t1.[Count], t2.MinDate, t2.ID
from (
select name, count(*) as [Count]
from Table_1
group by name
) AS t1
cross apply (
select top 1 DateOf as MinDate, ID
from Table_1 t2
where t1.Name = t2.Name
order by DateOf
) as t2
SELECT T1.Name,A.[COUNT],MIN(DateOf)
FROM Your_table_Name T1
JOIN
(
SELECT COUNT(*) [COUNT],T2.Name [Name]
FROM Your_table_Name T2
GROUP BY T2.Name
)A ON A.Name = T1.Name
GROUP BY T1.Name,A.[COUNT]

How to modify Row_Number syntax in SQL so that row numbers are assigned based on a condition?

I am using SQL Server 2014 and I have the following T-SQL query:
SELECT
d.PropertyCode,
b.ProfileID,
a.RSY_RESERVATIONSTAYID,
b.StatusCode, c2.CreatedOn AS 'Original CreatedOn',
a.RSY_UDFCHAR07 AS 'Original PMS No',
c2.ReservationStayID AS 'Original Resa ID',
b.CreatedOn,
b.PMSConfirmationNumber,
ROW_NUMBER() OVER (PARTITION BY b.ProfileID ORDER BY Count(*) DESC) AS rownum
FROM
ReservationStay b
LEFT JOIN
P5RESERVATIONSTAY a ON a.RSY_RESERVATIONSTAYID = b.ReservationStayID
LEFT JOIN
GuestNameInfo c on c.Reservationstayid = b.ReservationStayID
LEFT JOIN
RESERVATIONSTAY c2 on c2.PMSConfirmationNumber = a.RSY_UDFCHAR07
LEFT JOIN
GuestStaySummary d ON d.ReservationStayID = b.ReservationStayID
Here is an extract of my output:
PropertyCode ProfileID .... CreatedOn .... rownum
AXL 90072 2015-06-03 14:15:27.000 1
AXL 90072 2015-03-16 19:10:27.000 2
I need the rownum to be assigned based on the CreatedOn dates. In other words, for ProfileID 90072, rownum 1 is to be assigned where CreatedOn = 2015-03-16 and rownum 2 to be assigned where CreatedOn = 2015-06-03.
To simplify, rownum is assigned to a ProfileID in the ascending order of its CreatedOn dates.
How do I modify my row_number syntax to achieve this?
ROW_NUMBER() OVER (PARTITION BY b.ProfileID ORDER BY CreatedOn ASC) AS rownum

Resources