T-SQL multiple subqueries with JOIN and MAX(date) - sql-server

I have this query with 2 LEFT JOINS each with a sub-query.
The sub-queries should select the row with the latest date PointsChangeDate__c for a specific campaign e.g: PointsTypeCode__c = '1'.
Problem is that it is choosing what appears to be just random dates.
If I run just one JOIN / sub-query, then the result is correct, but when I add the 2nd JOIN / sub-query, then the results are incorrect and it seems to be pulling random dates.
I suspect my issue is with the type of JOIN I am using, but I cannot see why, because if I LEFT JOIN, then I am including all results (a._ContactKey) pulled from the first query and carrying that all the way through.
SELECT
a._ContactKey ContactId,
a.Full_Name_1__c Full_Name,
a.MC_Phone__c Mobile,
a.POSCustomerNumber__c POS_Customer_Number,
a.POSCustomerStatus__c POSCustomerStatus,
'IL' Locale,
a.Mobile_1__c Mobile_for_text,
g.TotalPoints__c TotalPoints_SourceMethod_1,
g.ValidUntil__c ValidUntil_SourceMethod_1,
g.date_1 PointsChangeDate__c_1,
h.TotalPoints__c TotalPoints_SourceMethod_2,
h.ValidUntil__c ValidUntil_SourceMethod_2,
h.date_2 PointsChangeDate__c_2
FROM
Contact_Salesforce AS a
LEFT JOIN
(SELECT
Contact__c,
TotalPoints__c,
ValidUntil__c,
MAX(PointsChangeDate__c) date_1
FROM
Member_points__c_Salesforce
WHERE
PointsTypeCode__c = '1'
GROUP BY Contact__c , TotalPoints__c , ValidUntil__c, PointsChangeDate__c) AS g ON a._ContactKey = g.Contact__c
LEFT JOIN
(SELECT Contact__c,
TotalPoints__c,
ValidUntil__c,
MAX(PointsChangeDate__c) date_2
FROM
Member_points__c_Salesforce
WHERE
PointsTypeCode__c = '2'
GROUP BY Contact__c , TotalPoints__c , ValidUntil__c, PointsChangeDate__c) AS h ON h.Contact__c = g.Contact__c
LEFT JOIN
SMS_Unsubscribe AS c ON REPLACE(CONCAT('972',
RIGHT(RTRIM(LTRIM(a.Mobile_1__c)), 9)),
'-',
'') = c.Mobile
LEFT JOIN
Member_Segments__c_Salesforce AS b ON b.Contact__c = a._ContactKey
WHERE
a.Mobile_1__c IS NOT NULL
AND c.Mobile IS NULL
AND a.POSCustomerStatus__c = '0'
AND b.IsActive__c = 'true'
GROUP BY a._ContactKey , a.Full_Name_1__c , a.MC_Phone__c , a.POSCustomerNumber__c , a.POSCustomerStatus__c , Locale , a.Mobile_1__c , a.Mobile_1__c , b.SegmentTypeID__c, b.SegmentTypeDescription__c , b.ToDate__c , g.TotalPoints__c , g.ValidUntil__c, g.date_1
,h.TotalPoints__c , h.ValidUntil__c, h.date_2

Related

Filter by date on view is very slow

I have an inkling what's causing the slowdown. The issue is that I'm not sure what the correct solution.
I have the following underlying query in my view (named vMemberListByDate):
select
mpi.membermpi
, mpi.accountnumber
, mpi.MemberKey
, membergroupkey = m.groupkey
, mpi.accountkey
, cr.CreditScore
, m.HashedSSN
, OpenDate = mpi.AccountOpenDate
, Closedate = mpi.AccountCloseDate
, Tenure = DATEDIFF(MONTH, mpi.AccountOpenDate, mpi.SnapshotDate)
, AgeIndex = db.Sort
, mpi.SnapshotDate
, ShareBalanceAmt = fmad.TotalShareBalance
, cr.TotalUnsecuredBalance
, CreditLineBalance = fmad.CreditLineLoanBalance
, MortgageBalance = fmad.FirstMortgageLoanBalance + fmad.SecondMortgageLoanBalance
, cr.RevolvingBalance
, IsActive
, fmad.LastChargeOffDate
, fmad.TotalChargeOffCnt
into
#Results
from
EDW.Global.vFactMemberMPIDaily as mpi (nolock)
inner join
EDW.Global.DimMember as m (nolock) on m.MemberKey = mpi.MemberKey
inner join
EDW.Global.FactMemberAccountDaily as fmad (nolock) on fmad.AccountKey = mpi.AccountKey
and fmad.SnapshotDate = mpi.SnapshotDate
left join
EDW.Global.DimBand as db (nolock) on isnull(datediff(year, BirthDate, mpi.SnapshotDate), -1) between db.LowValue and db.HighValue
and db.GroupDescription = 'Age 2'
left join
EDW.Global.DimCredit cr (nolock) on cr.HashedSSN = mpi.HashedSSN
and mpi.SnapshotDate between cr.StartDate and cr.EndDate
I select from the view as follows:
select count(*)
from ML.vMemberListByDate
where SnapshotDate = '11/1/2022'
It runs excruciatingly slow at 26 minutes total.
However, if I apply this date filter directly in the view's query code, it runs in 10 seconds. My assumption for the slowdown is that the view returns every row possible before filtering, therefore causing the slowdown.
Is there anyway around this?

Resulting nulls on full join not being replaced

I have a set of select queries using full join (required) and would like to replace the resulting nulls with something else (in the following example, it should be "empty").
For the first column (and all others, honestly) I have tried using isnull(), coallesce(), case when and even try_convert, but the result is always null. I'm ok with null, as in this particular case means that the results from the first query don't exist the second query, which is my goal.
There are following, identical queries, also full join 'd, so a line in the first query may not be in the second query but may be in the third of fourth queries.
Here is the select statement
SELECT *
FROM (SELECT Isnull(1, 'empty') AS SubGroup
, table2.lineintid AS OrderByThis2nd
, table2.HeaderStamp AS HeaderLink
, table2.linestamp AS LineID
, table2.lprocessname AS LineProcName
, table2.lprocessno AS LineProcNumber
, table2.productid AS ProdId
, table2.prodamount AS QTT
, table2.prodval AS UnitPrice
FROM table2 (nolock)
INNER JOIN table1 ON table2.headerstamp = table1.headerstamp
WHERE table1.lprocessname = 'Phase 1')Proc1L
FULL JOIN (SELECT Isnull(2, 'empty') AS SubGroup
, table2.lineintid AS OrderByThis2nd
, table2.linestamp AS LineID
, table2.prevlstamp AS PrecedingLine
, table2.lprocessname AS LineProcName
, table2.lprocessno AS LineProcNumber
, table2.productid AS ProdId
, table2.prodamount AS QTT
, table2.prodval AS UnitPrice
FROM table2 (nolock)
INNER JOIN table1 ON table2.headerstamp = table1.headerstamp
WHERE table1.lprocessname = 'Phase 2'
AND Year(table2.linedate) = '2018')Proc2L ON Proc1L.LineID = Proc2L.PrecedingLine
ORDER BY 1 DESC
, 2
This database is in MS SQL 2014.
Any ideas are appreciated. Thank you very much!
Try using ISNULL function in the outer query. Instead of
select * from
use
Select isnull(col1, 'x'), etc
from

Turn Date into Column with Pivot

Im not good at using Pivot but i think that's the only way to solve my Problem.
I have this SQL
SELECT DISTINCT ADR_Adressen.AdressNrADR
, LEFT(ADR_Adressen.Name, 3) AS Name
, LEFT(ADR_Adressen.Vorname, 3) AS Vorname
, CRM_Aufgaben.TerminVon
, LAG_Artikel.ArtikelNrLAG
, CRM_AufgabenLink.MitNrPRO
FROM ADR_Adressen
INNER JOIN PRO_Auftraege ON ADR_Adressen.AdressNrADR = PRO_Auftraege.Kunde
INNER JOIN CRM_Aufgaben ON PRO_Auftraege.AuftragNrPRO = CRM_Aufgaben.AuftragNrPRO
INNER JOIN CRM_Status ON CRM_Aufgaben.StatusCRM = CRM_Status.StatusCRM
INNER JOIN LAG_Artikel ON CRM_Aufgaben.ArtikelNrLAG = LAG_Artikel.ArtikelNrLAG
INNER JOIN ADR_GruppenLink ON ADR_Adressen.AdressNrADR = ADR_GruppenLink.AdressNrADR
INNER JOIN ADR_Gruppen ON ADR_GruppenLink.GruppeADR = ADR_Gruppen.GruppeADR
INNER JOIN CRM_AufgabenLink ON CRM_Aufgaben.AufgabenNrCRM = CRM_AufgabenLink.AufgabenNrCRM
WHERE { d '2016-03-07'} <= CRM_Aufgaben.TerminVon
AND { d '2016-03-11'} + 1 >= CRM_Aufgaben.TerminBis
AND CRM_AufgabenLink.MitNrPRO != 0
AND ADR_Gruppen.GruppeADR IN ( 'KIND' )
This is my result:
My wish is to get a Output like this:
The different Dates in TerminVon has to be Columns with the Values from ArtikelNrLAG+MitNrPRO. If the same AdressNrADR has more then one TerminVon on the same Date i have to make more rows. (Example where Name = Boc,Alt)
Can someone help me please =)
To PIVOT what you have, you can use a query similar to this.
SELECT AdressNrADR,
Name,
Vorname,
[2016-03-07],
[2016-03-08],
[2016-03-09],
[2016-03-10],
[2016-03-11]
FROM (
SELECT DISTINCT
ADR_Adressen.AdressNrADR,
LEFT(ADR_Adressen.Name,3) AS Name,
LEFT(ADR_Adressen.Vorname,3) AS Vorname,
CONVERT(VARCHAR(10), CRM_Aufgaben.TerminVon, 120) AS TerminVon, -- Convert date to yyyy-mm-dd format
LAG_Artikel.ArtikelNrLAG + '+' + CRM_AufgabenLink.MitNrPRO AS [Value], -- Combine column values
ROW_NUMBER() OVER
(PARTITION BY AdressNrADR,
LEFT(ADR_Adressen.Name,3),
LEFT(ADR_Adressen.Vorname,3),
CAST(CRM_Aufgaben.TerminVon AS DATE)
ORDER BY CRM_Aufgaben.TerminVon) Rn -- So we can get 1 row per time value
FROM ADR_Adressen
INNER JOIN PRO_Auftraege ON ADR_Adressen.AdressNrADR = PRO_Auftraege.Kunde
INNER JOIN CRM_Aufgaben ON PRO_Auftraege.AuftragNrPRO = CRM_Aufgaben.AuftragNrPRO
INNER JOIN CRM_Status ON CRM_Aufgaben.StatusCRM = CRM_Status.StatusCRM
INNER JOIN LAG_Artikel ON CRM_Aufgaben.ArtikelNrLAG = LAG_Artikel.ArtikelNrLAG
INNER JOIN ADR_GruppenLink ON ADR_Adressen.AdressNrADR = ADR_GruppenLink.AdressNrADR
INNER JOIN ADR_Gruppen ON ADR_GruppenLink.GruppeADR = ADR_Gruppen.GruppeADR
INNER JOIN CRM_AufgabenLink ON CRM_Aufgaben.AufgabenNrCRM = CRM_AufgabenLink.AufgabenNrCRM
WHERE { d '2016-03-07'} <= CRM_Aufgaben.TerminVon
AND { d '2016-03-11'} + 1 >= CRM_Aufgaben.TerminBis
AND CRM_AufgabenLink.MitNrPRO != 0
AND ADR_Gruppen.GruppeADR IN ('KIND')
) t
PIVOT (
MAX([Value])
FOR TerminVon IN ([2016-03-07],[2016-03-08],[2016-03-09],[2016-03-10],[2016-03-11])
) p
If you get that query to work. Your next step would be to make it Dynamic.
The difficult part of using t-sql's pivot functionality is that the output column names have to be hard coded. In your example we would need to know the value of each date and use that in the query in order to get the matching values by date. Fortunately other fine developers have experienced this frustration for us and have created scripts that will generate a dynamic pivot. I have included two links that will help you on your way.
https://www.mssqltips.com/sqlservertip/2783/script-to-create-dynamic-pivot-queries-in-sql-server/
http://sqlhints.com/2014/03/18/dynamic-pivot-in-sql-server/

how to join sql tables in my desired way

I have 2 sql tables named mtblAttendance and mtblLeave_Data.
I need to get the all dates from mtblLeave_Data when User was on leave depending on absent in mtblAttendance.
In my mtblAttendance for every leave there is a row, but if a user on leave for a period so there is no unique row, there are just two columns Leave_From and Leave_To (or it may be a single entry where Leave_From= Leave_To).
For getting the absent dates of user I wrote the query
USE [ILeave]
ALTER procedure [dbo].[Attendance_Report]
#Date1 datetime,
#Date2 datetime,
#User_Id nvarchar(50)
as begin
SELECT distinct
a.Sno,
a.[Login_Date],
a.[Week_Day],
a.[In_Time],
a.[Out_Time],
a.Attendance_Status,
a.Half_Full,
a.Leave_Type,
(convert(varchar(max),floor (abs(cast(datediff(mi, a.Out_Time, a.In_Time) AS int) / 60)))+ '.'+ convert(varchar(max),(abs(cast(datediff(mi, a.Out_Time, a.In_Time) AS int) % 60)))) as Hrs
, l.[Sno]
, l.[Leave_ID]
, l.[User_Id]
, l.[Dept_To]
, l.[Leave_Type]
, l.[Total_Leave_HR]
, l.[Leave_From]
, l.[Leave_To]
, l.[Leave_Half_Full]
, l.[Comments]
, l.[Leave_Status]
FROM
[mtblAttendance] a
LEFT JOIN [mtbl_Leave_Data] l
ON a.[Login_Date] BETWEEN l.[Leave_From] AND l.[Leave_To]
AND l.[User_Id] = a.[User_Id] where a.Login_Date between #Date1 and #Date2 and a.User_Id=#User_Id order by Login_Date
end
The following query should return the leave record assigned
SELECT
a.[Login_Date]
, l.[Sno]
, l.[Leave_ID]
, l.[User_Id]
, l.[Dept_To]
, l.[Leave_Type]
, l.[Total_Leave_HR]
, l.[Leave_From]
, l.[Leave_To]
, l.[Leave_Half_Full]
, l.[Comments]
, l.[Leave_Status]
FROM
[mtblAttendance] a
LEFT JOIN [mtbl_Leave_Data] l ON a.[Login_Date] BETWEEN l.[Leave_From] AND l.[Leave_To]
WHERE
a.User_Id = 'sasi'
AND a.Attendance_Status='A'
I put it into a fiddle, but with no data so all I can say is that the query parses.
As someone has previously stated, it is common to have tables with dates in, whereby queries requiring every date in a 2 year period can quickly be assessed.
Updated SQL:
SELECT DISTINCT
a.[Login_Date]
, l.[Sno]
, l.[Leave_ID]
, l.[User_Id]
, l.[Dept_To]
, l.[Leave_Type]
, l.[Total_Leave_HR]
, l.[Leave_From]
, l.[Leave_To]
, l.[Leave_Half_Full]
, l.[Comments]
, l.[Leave_Status]
FROM
[mtblAttendance] a
LEFT JOIN [mtbl_Leave_Data] l
ON a.[Login_Date] BETWEEN l.[Leave_From] AND l.[Leave_To]
AND l.[userId] = a.[user_id] -- Ensure only attendance/leave for the same user being linked
WHERE
a.User_Id = 'sasi'
AND a.Attendance_Status='A'
Join expressions aren't limited to using the equals sign. Use "between" in the join expression. Something along these (untested) lines should work.
select distinct A.Login_Date
from mtblAttendance A
inner join mtbl_Leave_Data L
on A.User_id = L.User_id
and A.Login_date between L.Leave_From and L.Leave_To
where A.User_Id = 'sasi' AND A.Attendance_Status='A'
Depending on what you're trying to do, you might need to change the inner join to a left outer join. A left outer join will preserve all login dates from mtblAttendance, regardless of whether they satisfy the join condition. (Those rows will be filtered by the WHERE clause, of course.)

Group by - multiple conditions - MySQL

How can I combine 2 group by conditions? I have records for each id for every hour in a day and I want to group information by first id and all records for that id in that day then second id and all records for that in the day.
My sample query is this:
SELECT
r.name
, r.network
, r.namestring
, i.name
, i.description
, r.rid
, i.id
, d.dtime
, d.ifInOctets
FROM router AS r
INNER JOIN interface AS i
ON r.rid = i.rid
INNER JOIN 1279080000_1_60 AS d
ON i.id = d.id
AND dtime BETWEEN 1279113600 AND 1279115400
WHERE r.network = "ITPN"
AND i.status = "active"
GROUP BY i.id AND d.dtime // each id with all its dtime
This always ends up giving me an aggregate value for that id.
Any idea what I could use??? I don't want to sum up all values.
Thank you,
To group by multiple expressions you should separate them with a comma, not AND:
GROUP BY i.id, d.dtime
You should also ensure that two records form the same day have the same value of dtime. It's not clear from your question whether this is or is not the case.

Resources