How to combine two rows into a single row in CTE - sql-server

I am getting data in a CTE like this.
Here I am getting two records for each ActivityId, what I want to achieve is two create a single record from the two rows. From the row where r2=1 I need only the RDate as Todate, ActualProgress as endprogress and PlannedProgress as endplannedprogress because these are the only values that will changed on both the rows.
I am getting this data in a CTE.
WITH CTE as(SELECT row_number() over (partition by pmaph.ActivityId order by date) r1, row_number() over (partition by pmaph.ActivityId order by date desc) r2,pma.PlanEndDate as PlanEndDate ,pma.PlanStartDate as PlanStartDate,pmaph.ActivityId,pmaph.ProjectMilestoneActivProgHist_Id as ProMileAvtiID,umd.UOM_Name as UomName, pmm.MilestoneName as MilestoneName,pma.ActivityName as ActivityName ,pmp.ProjectName as ProjectName, Replace((rtrim(ltrim(Convert(varchar(12),Cast(pmaph.Date as Datetime),106)))),' ','-') as RDate, isnull(pmaph.Actual_Progress,0) as ActualProgress,isnull(pmaph.Planned_Progress,0) as PlannedProgress FROM ProjectMilestoneActivityProgressHistory as pmaph left join dbo.PM_Project as pmp on pmaph.ProjectId=pmp.ProjectId left join dbo.PM_Activity as pma on pmaph.ActivityId=pma.ActivityId left join dbo.PM_Milestone as pmm on pmaph.MilestoneId=pmm.MilestoneId left join dbo.UOMDetail as umd on pma.UOM_Id=umd.UOM_Id where pmaph.Client_Id=#ClientId
)
select * from CTE where r1=1 or r2=1

You can do a Group By ActivityId and including a MAX(RDate) AND MAX(ActualProgress), it should return you only one-row per ActivityId.

Maybe this can give you a hint. Make sure to keep the format of your SQL.
;WITH CTE as
(
SELECT
row_number() over (partition by pmaph.ActivityId order by date) r1,
row_number() over (partition by pmaph.ActivityId order by date desc) r2,
pma.PlanEndDate as PlanEndDate ,
pma.PlanStartDate as PlanStartDate,
pmaph.ActivityId,
pmaph.ProjectMilestoneActivProgHist_Id as ProMileAvtiID,
umd.UOM_Name as UomName,
pmm.MilestoneName as MilestoneName,
pma.ActivityName as ActivityName,
pmp.ProjectName as ProjectName,
Replace((rtrim(ltrim(Convert(varchar(12),Cast(pmaph.Date as Datetime),106)))),' ','-') as RDate,
isnull(pmaph.Actual_Progress,0) as ActualProgress,
isnull(pmaph.Planned_Progress,0) as PlannedProgress
FROM
ProjectMilestoneActivityProgressHistory as pmaph
left join dbo.PM_Project as pmp on pmaph.ProjectId=pmp.ProjectId
left join dbo.PM_Activity as pma on pmaph.ActivityId=pma.ActivityId
left join dbo.PM_Milestone as pmm on pmaph.MilestoneId=pmm.MilestoneId
left join dbo.UOMDetail as umd on pma.UOM_Id=umd.UOM_Id
where
pmaph.Client_Id=#ClientId
)
SELECT
C1.*, -- Now you have all data in one row (by Activity), select whichever columns you want
C2.*
FROM
CTE AS C1
INNER JOIN CTE AS C2 ON
C1.ActivityId = C2.ActivityId AND
C2.r2 = 1 -- ... and join against the other row
WHERE
C1.r1 = 1 -- pick 1 row by Activity

Related

Print results side by side SQL Server

I have following result set,
Now with above results i want to print the records via select query as below attached image
Please note, I will have only two types of columns in output Present Employee & Absent Employees.
I tried using pivot tables, temporary table but cant achieve what I want.
One method would be to ROW_NUMBER each the the "statuses" and then use a FULL OUTER JOIN to get the 2 datasets into the appropriate columns. I use a FULL OUTER JOIN as I assume you could have a different amount of employees who were present/absent.
CREATE TABLE dbo.YourTable (Name varchar(10), --Using a name that doesn't require delimit identification
Status varchar(7), --Using a name that doesn't require delimit identification
Days int);
GO
INSERT INTO dbo.YourTable(Name, Status, Days)
VALUES('Mal','Present',30),
('Jess','Present',20),
('Rick','Absent',30),
('Jerry','Absent',10);
GO
WITH RNs AS(
SELECT Name,
Status,
Days,
ROW_NUMBER() OVER (PARTITION BY Status ORDER BY Days DESC) AS RN
FROM dbo.YourTable)
SELECT P.Name AS PresentName,
P.Days AS PresentDays,
A.Name AS AbsentName,
A.Days AS AbsentDays
FROM (SELECT R.Name,
R.Days,
R.Status,
R.RN
FROM RNs R
WHERE R.Status = 'Present') P
FULL OUTER JOIN (SELECT R.Name,
R.Days,
R.Status,
R.RN
FROM RNs R
WHERE R.Status = 'Absent') A ON P.RN = A.RN;
GO
DROP TABLE dbo.YourTable;
db<>fiddle
2 CTE's is actually far neater:
WITH Absents AS(
SELECT Name,
Status,
Days,
ROW_NUMBER() OVER (ORDER BY Days DESC) AS RN
FROM dbo.YourTable
WHERE Status = 'Absent'),
Presents AS(
SELECT Name,
Status,
Days,
ROW_NUMBER() OVER (ORDER BY Days DESC) AS RN
FROM dbo.YourTable
WHERE Status = 'Present')
SELECT P.Name AS PresentName,
P.Days AS PresentDays,
A.Name AS AbsentName,
A.Days AS AbsentDays
FROM Absents A
FULL OUTER JOIN Presents P ON A.RN = P.RN;

T-SQL Date Difference between rows using row number same table

This is my SQL Server view:
SELECT
ROW_NUMBER() OVER (PARTITION BY N.PHN
ORDER BY N.CheckDate ASC) AS RowNumber,*
FROM
(SELECT
dbo.Flu.Day AS CheckDate,
dbo.Flu.PHN AS PHN
FROM
dbo.Flu
LEFT OUTER JOIN
dbo.Patient ON dbo.Flu.PHN = dbo.Patient.PHN) AS N;
Result:
I am trying to find the difference between two consecutive dates in days for each PHN based on RowNumber; keeping in mind that some PHN will only have one RowNumber.
You can do it with LAG() window function:
SELECT f.Day AS CheckDate, f.PHN AS PHN,
DATEDIFF(
day,
COALESCE(LAG(f.Day) OVER (PARTITION BY f.PHN ORDER BY f.Day ASC), f.Day),
f.Day
) diff
FROM dbo.Flu f LEFT OUTER JOIN dbo.Patient p
ON f.PHN = p.PHN

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

rank function with sum

I have two tables.
abc(CID(pk), cname,)
order(order_id(pk), CID(fk), number_of_rentals)
I want to determine top 10 customers on the basis of number of movies they rented.
select
orders.cid,orders.no_rentals, abc.name,
rank() over (order by no_rentals desc) "rank"
from abc
inner join orders on orders.CID = abc.CID;
I used this query but it's not universal. How can I use sum function on number_of_rentals with this query?
Select Top 10
orders.cid
, abc.name
, SUM(orders.no_rentals) TotalRentals
, rank() over (order by SUM(orders.no_rentals) desc) [rank]
from abc
inner join orders on orders.CID = abc.CID
Group By orders.cid, abc.name
Order By TotalRentals DESC

RANK() Over Partition BY not working

When I run the code below the ROWID is always 1.
I need to the ID to start at 1 for each item with the same Credit Value.
;WITH CTETotal AS (SELECT
TranRegion
,TranCustomer
,TranDocNo
,SUM(TranSale) 'CreditValue'
FROM dbo.Transactions
LEFT JOIN customers AS C
ON custregion = tranregion
AND custnumber = trancustomer
LEFT JOIN products AS P
ON prodcode = tranprodcode
GROUP BY
TranRegion
,TranCustomer
,TranDocNo)
SELECT
r.RegionDesc
,suppcodedesc
,t.tranreason as [Reason]
,t.trandocno as [Document Number]
,sum(tranqty) as Qty
,sum(tranmass) as Mass
,sum(transale) as Sale
,cte.CreditValue AS 'Credit Value'
,RANK() OVER (PARTITION BY cte.CreditValue ORDER BY cte.CreditValue)AS ROWID
FROM transactions t
LEFT JOIN dbo.Regions AS r
ON r.RegionCode = TranRegion
LEFT JOIN CTETotal AS cte
ON cte.TranRegion = t.TranRegion
AND cte.TranCustomer = t.TranCustomer
AND cte.TranDocNo = t.TranDocNo
GROUP BY
r.RegionDesc
,suppcodedesc
,t.tranreason
,t.trandocno
,cte.CreditValue
ORDER BY CreditValue ASC
EDIT
All the credit values with 400 must have the ROWID set to 1. And all the credit values with 200 must have the ROWID set to 2. And so on and so on.
Do you need something like this?
with cte (item,CreditValue)
as
(
select 'a',8 as CreditValue union all
select 'b',18 union all
select 'a',8 union all
select 'b',18 union all
select 'a',8
)
select CreditValue,dense_rank() OVER (ORDER BY item)AS ROWID from cte
Result
CreditValue ROWID
----------- --------------------
8 1
8 1
8 1
18 2
18 2
In your code replace
,RANK() OVER (PARTITION BY cte.CreditValue ORDER BY cte.CreditValue)AS ROWID
by
,DENSE_RANK() OVER (ORDER BY cte.CreditValue)AS ROWID
You just don't have to use PARTITION, just DENSE_RANK() OVER (ORDER BY cte.CreditValue)
I think the problem is with the RANK() OVER (PARTITION BY clause
you have to partition it by item not by CreditValue
Try this
RANK() OVER (PARTITION BY cte.CreditValue ORDER BY cte.RegionDesc)AS ROWID
Edit: The issue here isn't actually the nesting of the subquery, it's potentially based on partition by having columns that truly make each row unique (or 1)
Rather than ranking within your complex query like this
select
rank() over(partition by...),
*
from
data_source
join table1
join table2
join table3
join table4
order by
some_column
Try rank() or row_number() on the resulting data set, not within it.
For example, using the query above, remove rank() and implement it this way:
select
rank() over(partition by...),
results.*
from (
select
*
from
data_source
join table1
join table2
join table3
join table4
order by
some_column
) as results

Resources