Putting SQL record values in a single row - sql

SQL isn't my expertise but, how do I write a SQL query that takes values from two different records and put them in one row? For example, I did a query on this EmployeeId, and I need the output values specifically from the Vacation_Type column for Adjust on 2016-07-01 (if it exists) and the Forward value from date 2016-08-01 (exists for every employee) ? Desired output would be:
26, SL, 547.58, -37.42
Not every employee would have an Adjust record, they have an Adjust when they are over the sick leave cap... and not everyone is over the cap. Thanks!

select
EV.EmployeeID,
EV.Vacation_Kind,
stuff( ( select ', ' + convert(varchar(30), EV1.Value) from EmployeeVacations EV1
where EV1.EmployeeID = EV.EmployeeID and EV1.Vacation_Kind = EV.Vacation_Kind
and EV1.VacationType in ('Adjust','Forward') and EV1.CreationDate IN (EV.CreationDate, dateadd(month, datediff(month, 0, dateadd(month, 1, EV.CreationDate)), 0))
for xml path('')
), 1, 1, '')
from
EmployeeVacations EV
where
EV.EmployeeID = 26 and EV.Vacation_Kind = 'SL'and VacationType = 'Adjust'
group by
EV.EmployeeID, EV.Vacation_Kind, EV.CreationDate

Since the 'Adjust' record isn't guaranteed to be there, you can use a LEFT OUTER JOIN. The following queries all employees, but you can uncomment the two lines if you really only want employee 26...
SELECT
CAST(fwd.EmployeeId AS varchar) + ', ' +
fwd.Vacation_Kind + ', ' +
CAST(fwd.[Value] as varchar) +
CASE WHEN adj.Value IS NULL THEN '' ELSE ', ' + CAST(adj.Value AS varchar) END
FROM
#employeeVacations fwd
LEFT OUTER JOIN #employeeVacations adj ON
adj.EmployeeId = fwd.EmployeeId)
AND
adj.Vacation_Kind = 'SL')
AND
adj.Vacation_Type = 'Adjust'
AND
adj.CreationDate = '2016-07-01'
WHERE
--fwd.EmployeeId = 26
--AND
fwd.Vacation_Kind = 'SL'
AND
fwd.Vacation_Type = 'Forward'
AND
fwd.CreationDate = '2016-08-01'

Related

Query Filter based on condition

I have this query (SQL Server 2019) and I actually wanted to add a filter to it to show only items where IssueStatusID = 1 (This column is based on int Data Type)
Can anyone help with this or point me in the right direction?
SELECT ID,
STRING_AGG(TRY_CONVERT(varchar, Issue#, 101) + ': ' + Notes + CHAR(13) + CHAR(10) + CHAR(13), CHAR(10)) WITHIN GROUP (ORDER BY Issue# DESC) AS IssueNotes
FROM (SELECT DISTINCT
ac4.ID,
nd.Notes,
nd.Issue#,
nd.IssueStatusID
FROM dbo.IssueTracking AS nd
INNER JOIN dbo.StatusUpdates AS ac4 ON ac4.ID = nd.ReleaseTrackerID) AS vNotes
GROUP BY ID;
Add the WHERE clause as shown below. The WHERE clause limits the data being returned.
SELECT ID,
STRING_AGG(TRY_CONVERT(varchar, Issue#, 101) + ': ' + Notes + CHAR(13) + CHAR(10) + CHAR(13), CHAR(10)) WITHIN GROUP (ORDER BY Issue# DESC) AS IssueNotes
FROM (SELECT DISTINCT
ac4.ID,
nd.Notes,
nd.Issue#,
nd.IssueStatusID
FROM dbo.IssueTracking AS nd
INNER JOIN dbo.StatusUpdates AS ac4 ON ac4.ID = nd.ReleaseTrackerID
WHERE nd.IssueStatusID = 1
) AS vNotes
GROUP BY ID;

SQL group by that combines the 2nd group and get distinct first group

The query currently returns
Bob President 5
Joe Programmer 3
Greg Programmer 2
Joe Janitor 6
Liz Programmer 6
But I want it to only have one row for each person like this
Bob President 5
Joe Programmer (or Janitor) 9
Greg Programmer 2
Liz Programmer 6
I am grouping by the Name and the Role now. How to I also get only distinct names and combine that value ?
SELECT
...
FROM
vw_QuickbaseAudit v
INNER JOIN [staging].[QuickbaseSystemUsers] qsu ON v.[user] = qsu.UserId
WHERE
Date_Modified >= #StartDate
AND Date_Modified < DATEADD(dd, 1, #EndDate)
GROUP BY
qsu.UserFirstName + ' ' + qsu.UserLastName,
qsu.RoleName
ORDER BY
RoleName,
NumberOfEditTotal DESC;
SELECT qsu.UserFirstName + ' ' + qsu.UserLastName AS "FullName",
STUFF((SELECT '/' + r.RoleName FROM QuickbaseSystemUsers r on r.UserID = qsu.UserID FOR XML PATH('')), 1, 2, '') as "Roles",
NumberOfEditTotal << [however you calced this]
FROM vw_QuickbaseAudit v
INNER JOIN [staging].[QuickbaseSystemUsers] qsu ON v.[user] = qsu.UserId
WHERE Date_Modified >= #StartDate AND Date_Modified < DATEADD(dd, 1, #EndDate)
GROUP BY qsu.UserFirstName + ' ' + qsu.UserLastName,
STUFF((SELECT '/' + r.RoleName FROM QuickbaseSystemUsers r on r.UserID = qsu.UserID FOR XML PATH('')), 1, 2, '')
ORDER BY RoleName, NumberOfEditTotal DESC;
Some variant of that
(edited: missed the additional STUFF command in the group by)
I am interpreted this:
Joe Programmer (or Janitor) 9
As saying that either of these is okay:
Joe Programmer 9
Joe Janitor 9
The query would look like this:
SELECT qsu.UserFirstName + ' ' + qsu.UserLastName,
MIN(qsu.RoleName) as RoleName
SUM(NumberOfEditTotal) as NumberOfEditTotal
FROM vw_QuickbaseAudit v JOIN
[staging].[QuickbaseSystemUsers] qsu
ON v.[user] = qsu.UserId
WHERE Date_Modified >= #StartDate AND
Date_Modified < DATEADD(day, 1, #EndDate)
GROUP BY qsu.UserFirstName + ' ' + qsu.UserLastName
ORDER BY RoleName, NumberOfEditTotal DESC;
It is unclear how NumberOfEditTotal is calculated.
EDIT:
Appending all the rows in tricky. In the latest versions of SQL Server, you can use STRING_AGG() as a string aggregation functions. That is simple.
My recommendation in early versions would be a compromise. So values might be:
Janitor
Janitor and Programmer
Janitor, Programmer, and others
You can do this with:
(case when min(rolename) = max(rolename)
then min(rolename)
when count(distinct rolename) = 2
then min(rolename) + ' and ' + max(rolename)
else min(rolename) + ', ' + max(rolename) + ' others'
end)
Basically you need to remove RoleName from the GROUP BY clause. You can do string aggregation with STRING_AGG():
SELECT
qsu.UserFirstName + ' ' + qsu.UserLastName,
STRING_AGG(RoleName, ' or ') RoleNames,
...
FROM
vw_QuickbaseAudit v
INNER JOIN [staging].[QuickbaseSystemUsers] qsu ON v.[user] = qsu.UserId
WHERE
Date_Modified >= #StartDate
AND Date_Modified < DATEADD(dd, 1, #EndDate)
GROUP BY
qsu.UserFirstName,
qsu.UserLastName
ORDER BY
NumberOfEditTotal DESC;

CAST number as varchar, then update into another table as date

I'll as brief as possible, but start by saying I'm a network guy, not a DBA.
SQL Server Enterprise 11.0.5343
Scenario - Need to get three columns (each with a part of a date) together, and then update another table with the full date.
Source table: UT210AP
Columns:
UTONMM (Utility On Month - 2 digit)
UTONDD (Utility On Day - 2 digit)
UTONYY (Utility On Year - 2 digit)
UTONCV (Utility On Century - 0 = 19xx, 1 = 20xx)
I can select the data into a "date" with this code (the source data is on an IBM AS/400 linked server)
CAST(UTONMM as varchar) + '/' +
CAST(UTONDD as varchar) + '/' +
CASE WHEN UTONCV = '1'
THEN
RIGHT('20' + CONVERT(varchar(4), RIGHT('00' + CONVERT(varchar(4),UTONYY),2)),4)
ELSE
RIGHT('19' + CONVERT(varchar(4), UTONYY),4)
END AS UTON
And I get these results in the column I named "UTON":
4/6/1994
7/1/1988
11/14/1990
6/6/2014
QUESTION:
I have a nightly import job that runs, and I need to get the "date" (like 4/6/1994) into a field called TIME_CONNECT as part of this update statement from the job:
Update [responder].[Temp_RX_CUSTOMERS]
set CustomerID = lf.UTCSID
from [responder].[Temp_RX_CUSTOMERS] LEFT Outer Join
[HTEDTA].[THOR].[HTEDTA].UT210AP lf ON [responder].[Temp_RX_CUSTOMERS].LocationID = lf.UTLCID
where lf.UTOFMM = 0
The "UTOFMM" in the code above is "Utility Off Month", I don't even care about checking for it's value, I just want to get the "UTON" date from the top select statement into the "TIME_CONNECT" field in the "Temp_RX_CUSTOMERS" field.
Is this what you want? This copies the value into the field, assuming time_connect is a string.
Update [responder].[Temp_RX_CUSTOMERS]
set CustomerID = lf.UTCSID,
time_connect = (CAST(UTONMM as varchar) + '/' +
CAST(UTONDD as varchar) + '/' +
(CASE WHEN UTONCV = '1'
THEN RIGHT('20' + CONVERT(varchar(4), RIGHT('00' + CONVERT(varchar(4),UTONYY),2)),4)
ELSE RIGHT('19' + CONVERT(varchar(4), UTONYY),4)
END)
)
from [responder].[Temp_RX_CUSTOMERS] LEFT Outer Join
[HTEDTA].[THOR].[HTEDTA].UT210AP lf
ON [responder].[Temp_RX_CUSTOMERS].LocationID = lf.UTLCID
where lf.UTOFMM = 0;
If time_connect is a date/datetime data type, you can use datefromparts() (available in SQL Server 2012+):
Update [responder].[Temp_RX_CUSTOMERS]
set CustomerID = lf.UTCSID,
time_connect = DATEFROMPARTS(1800 + UTONCV * 100 + UNONYY,
UTONMM, UTONDD)
from [responder].[Temp_RX_CUSTOMERS] LEFT Outer Join
[HTEDTA].[THOR].[HTEDTA].UT210AP lf
ON [responder].[Temp_RX_CUSTOMERS].LocationID = lf.UTLCID
where lf.UTOFMM = 0;

Conversion failed when converting the nvarchar value '29449,29446,29450,29534' to data type int

I am create a stored procedure in SQL and I get the following error when I execute the query:
Conversion failed when converting the nvarchar value '11021,78542,12456,24521' to data type int.
Any idea why?
SELECT
A.Art_ID, A.Title
FROM
Art A
INNER JOIN
Iss I ON A.Iss_ID = I.Iss_ID
INNER JOIN
Sections S ON A.Section_ID = S.Section_ID
INNER JOIN
iPadSec IPS ON A.Sec_ID = IPS.Sec_ID
WHERE
A.Art_ID IN (SELECT CAST(Art_IDs AS int) /***error happens here***/
FROM Book_Art b
WHERE Sub_ID = 68)
AND I.Iss > dateadd(month, -13, getdate())
AND A.Active = 1
AND IPS.Active = 1
AND A.PDate <= getdate()
ORDER BY
PDate DESC, Art_ID DESC;
You cannot do what you want using in. First, it is a really bad idea to store ids in lists in strings. You should be using a junction table.
That said, sometimes this is necessary. You can rewrite this line of code as:
EXISTS (SELECT 1 /***error happens here***/
FROM Book_Art b
WHERE Sub_ID = 68 AND
',' + Art_IDs + ',' LIKE '%,' + cast(A.Art_ID as varchar(255)) + ',%'
)
However, the performance would generally be on the lousy side and there is little prospect of speeding this up without fixing the data structure. Use a junction table instead of a string to store lists.
Adding this line works for me.
declare #ids varchar(1000)
select #ids = art_ids from book_art where sub_id = #Sub_ID
EXECUTE ( 'SELECT A.Art_ID, A.Title'
+ ' FROM Art A'
+ ' INNER JOIN Iss I ON A.Iss_ID = I.Iss_ID'
+ ' INNER JOIN Sections S ON A.Section_ID = S.Section_ID'
+ ' INNER JOIN iPadSec IPS ON A.Sec_ID = IPS.Sec_ID'
+ ' WHERE A.Art_ID IN (' + #ids + ')'
+ ' AND I.Iss > dateadd(month, -13, getdate())'
+ ' AND A.Active = 1'
+ ' AND IPS.Active = 1'
+ ' AND A.PDate <= getdate()'
+ ' ORDER BY PDate DESC,'
+ ' Art_ID DESC;'
)
END
Thank you all for your help :)

obtain an average of days an issue is open

I have a table with a 'call open date' column (actual_log_date) which I convert, using
convert(char(10), actual_log_date, 103) 'Call Logged'
to show calls logged on 01/02/2012 for example.
There is also a close time column (close_time) for which I run the same convert,
convert(char(10), close_time, 103) 'Call Closed'
to show calls closed on 12/02/2012.
Now, I need to produce a script to get the average number of days calls have been open. I can run
(select datediff(dd, c.actual_log_date, c.close_time)) as 'Days Open'
to create the column which shows the call was open for 12 days or whatever, but now need to count all the records and obtain the average sum at the end of the results, which is where I am stuck!
I forgot to say I can't create a temp table as I only have read only rights.
Sorry, I'm new here so not sure on the protocol but here is the script if it's easier to answer seeing what I'm working with;
select
convert(char(10),actual_log_date,103) 'Call Logged',
call_number 'Call #',
short_problem 'Issue Description',
Customer = (select surname + ', ' + first_name from i.ar_user_attributes where ref = user_ref ),
Officer = (select surname + ', ' + first_name from i.su_help_centre where ref = resolve_officer),
Department = (select name from i.su_support_group where ref = resolve_group),
convert(char(10),close_time,103) 'Closed',
(select datediff(hh,c.actual_log_date,c.close_time)) as 'Hours Open',
(select datediff(dd,c.actual_log_date,c.close_time)) as 'Days Open'
from
i.cl_call_logging c
where
c.resolve_time between
convert(datetime,convert(varchar,month(dateadd(m,-1,getdate()))) + '/01/' + convert(varchar,year(dateadd(m,-1,getdate())))) and
convert(datetime,convert(varchar,month(getdate())) + '/01/' + convert(varchar,year(getdate())))
and c.resolve_group in ('48', '60')
Thanks again!
You can use a with query, like this:
insert into #temp (CallOpen, CallClosed)
values
('2012-01-02', '2012-02-12'),
('2012-01-05', '2012-02-12'),
('2012-01-07', '2012-02-05'),
('2012-01-14', '2012-02-03'),
('2012-02-10', '2012-03-15')
;with T1 as
(
select
CallOpen,
CallClosed,
DATEDIFF(dd, callopen, callclosed) as DaysOpen
from #temp
)
select AVG(daysopen) from T1
Using your full script, try this:
;with T1 as
(
select
convert(char(10),actual_log_date,103) 'Call Logged',
call_number 'Call #',
short_problem 'Issue Description',
Customer = (select surname + ', ' + first_name from i.ar_user_attributes where ref = user_ref ),
Officer = (select surname + ', ' + first_name from i.su_help_centre where ref = resolve_officer),
Department = (select name from i.su_support_group where ref = resolve_group),
convert(char(10),close_time,103) 'Closed',
(select datediff(hh,c.actual_log_date,c.close_time)) as 'Hours Open',
(select datediff(dd,c.actual_log_date,c.close_time)) as 'Days Open'
from #cl_call_logging c
where
c.resolve_time between
convert(datetime,convert(varchar,month(dateadd(m,-1,getdate()))) + '/01/' + convert(varchar,year(dateadd(m,-1,getdate())))) and
convert(datetime,convert(varchar,month(getdate())) + '/01/' + convert(varchar,year(getdate())))
and c.resolve_group in ('48', '60')
)
select AVG([Days Open]) from T1