Concat records SQL Server - sql-server

I have this query and i can't make it to concatanete the second column.
SELECT
Container.UIDaughterPlateId AS UIDaughterPlateId,
AllLastOperationInfo.OperationShortLabel AS lab
FROM
((InSite.UIDaughterPlate AS Container
INNER JOIN
InSite.UIDaughterPlateInfo AS UIDaughterPlateInfo ON (Container.UIDaughterPlateInfoId = UIDaughterPlateInfo.UIDaughterPlateInfoId ))
LEFT OUTER JOIN
(SELECT
UIOperationInfo.UIOperationInfoId as OperationInfoId,
UIOperationInfo.ParentId as DaughterPlateInfoId,
UIOperationInfo.Status as Status,
Operation.ShortLabel as OperationShortLabel,
UIOperationInfo.IsLast
FROM
UIOperationInfo
INNER JOIN
Operation ON Operation.OperationId = UIOperationInfo.UIOperationId
WHERE
UIOperationInfo.IsLast = 1 and Status = 'A RĂ©aliser') AllLastOperationInfo ON (UIDaughterPlateInfo.UIDaughterPlateInfoId = AllLastOperationInfo.DaughterPlateInfoId ))
ORDER BY
Container.UIDaughterPlateName DESC
The current results are
-------------------------
| UIDaughterPlateId | Lab |
|-------------------------|
| 42 | MD |
| 42 | MC |
| 47 | MC |
| 67 | MA |
| 67 | MB |
| 67 | MC |
-------------------------
And I want to have these results
-------------------------------
| UIDaughterPlateId | Lab |
|-------------------------------|
| 42 | MC MD |
| 47 | MC |
| 67 | MA MB MC |
-------------------------------
I've tried several examples that I found in other post but without success.
Can someone help me?
Thank you

You can try the following code:
-- Create demo data
CREATE TABLE #temp(UIDaughterPlateId int, Lab nvarchar(5))
INSERT INTO #temp(UIDaughterPlateId, Lab)
VALUES (42,N'MD'),(42, N'MC'),(47, N'MC'),(67, N'MA'),(67, N'MB'),(67, N'MC')
-- Your part:
SELECT DISTINCT t.UIDaughterPlateId, LEFT(dat.Lab,LEN(dat.Lab)-1) as Lab
FROM #temp AS t
OUTER APPLY (
SELECT s.Lab+N', '
FROM #temp as s
WHERE s.UIDaughterPlateId = t.UIDaughterPlateId
FOR XML PATH(N'')
) as dat(Lab)
-- Cleanup
DROP TABLE #temp
On the given input:
UIDaughterPlateId Lab
----------------- -----
42 MD
42 MC
47 MC
67 MA
67 MB
67 MC
This is the result of the query:
UIDaughterPlateId Lab
----------------- ----------
42 MD, MC
47 MC
67 MA, MB, MC
If you want to adapt it to your table structure just use a CTE for this.
WITH data AS(
-- your code from your question
SELECT
Container.UIDaughterPlateId AS UIDaughterPlateId,
AllLastOperationInfo.OperationShortLabel AS lab
FROM
((InSite.UIDaughterPlate AS Container
INNER JOIN
InSite.UIDaughterPlateInfo AS UIDaughterPlateInfo ON (Container.UIDaughterPlateInfoId = UIDaughterPlateInfo.UIDaughterPlateInfoId ))
LEFT OUTER JOIN
(SELECT
UIOperationInfo.UIOperationInfoId as OperationInfoId,
UIOperationInfo.ParentId as DaughterPlateInfoId,
UIOperationInfo.Status as Status,
Operation.ShortLabel as OperationShortLabel,
UIOperationInfo.IsLast
FROM
UIOperationInfo
INNER JOIN
Operation ON Operation.OperationId = UIOperationInfo.UIOperationId
WHERE
UIOperationInfo.IsLast = 1 and Status = 'A RĂ©aliser') AllLastOperationInfo ON (UIDaughterPlateInfo.UIDaughterPlateInfoId = AllLastOperationInfo.DaughterPlateInfoId ))
ORDER BY
Container.UIDaughterPlateName DESC
)
SELECT DISTINCT t.UIDaughterPlateId, LEFT(dat.Lab,LEN(dat.Lab)-1) as Lab
FROM data AS t
OUTER APPLY (
SELECT s.Lab+N', '
FROM data as s
WHERE s.UIDaughterPlateId = t.UIDaughterPlateId
FOR XML PATH(N'')
) as dat(Lab)

Create a view A using your current results, and execute
SELECT UIDaughterPlateId, replace(group_concat(lab),',',' ') AS lab FROM A GROUP BY UIDaughterPlateId

Related

Joining Returns Duplicate Rows

I'm having problems figuring out how to reconcile records against two tables. Table 1 will contain records from one system and Table 2 will contain records from another system. Both tables will have an ID column unique to itself. It's possible that Table 1 will contain similar records, but with a different ID and the same for Table 2.
Table 1
ID | Acct_Num | Amount | Dt
---------+-----------+---------+-------------
96 | 5836 | 75 | 2020-04-02
100 | 5836 | 75 | 2020-04-02
Table 2
ID | Acct_Num | Amount | Dt
---------+-----------+---------+-------------
3 | 5836 | 75 | 2020-04-02
39 | 5836 | 75 | 2020-04-03
When I try to join on Acct_Num and Amount, the result returns 4 records, both records in Table 1 matching to both records in Table2.
SELECT * FROM Table1 t1 INNER JOIN Table 2 ON t1.Acct_Num = t2.Acct_Num AND t1.Amount = t2.Amount
ID | Acct_Num | Amount | Dt | ID | Acct_Num | Amount | Dt
---------+-----------+---------+-------------+-----------+-----------+---------+-------------
96 | 5836 | 75 | 2020-04-02 | 3 | 5836 | 75 | 2020-04-02
96 | 5836 | 75 | 2020-04-02 | 39 | 5836 | 75 | 2020-04-03
100 | 5836 | 75 | 2020-04-02 | 3 | 5836 | 75 | 2020-04-02
100 | 5836 | 75 | 2020-04-02 | 39 | 5836 | 75 | 2020-04-03
I understand this is how joins work, but what I'm looking to accomplish is to have a record on the left to match with just one record on the right. I don't care which one. The next record on the left will then match against the next available record on the right. Ending result as:
ID | Acct_Num | Amount | Dt | ID | Acct_Num | Amount | Dt
---------+-----------+---------+-------------+-----------+-----------+---------+-------------
96 | 5836 | 75 | 2020-04-02 | 3 | 5836 | 75 | 2020-04-02
100 | 5836 | 75 | 2020-04-02 | 39 | 5836 | 75 | 2020-04-03
I'm a bit lost on how I could accomplish this. Any suggestion would be helpful!
If you really don't care how the records get paired up, we could try doing a full outer join using ROW_NUMBER ordered by the ID column:
WITH cte1 AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY Acct_Num ORDER BY ID) rn
FROM Table1
),
cte2 AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY Acct_Num ORDER BY ID) rn
FROM Table2
)
SELECT t1.ID, t1.Acct_Num, t1.Amount, t1.Dt, t2.ID, t2.Acct_Num, t2.Amount, t2.Dt
FROM cte1 t1
FULL OUTER JOIN cte2 t2
ON t1.Acct_Num = t2.Acct_Num AND
t1.rn = t2.rn
ORDER BY
t1.Acct_Num,
t1.ID;
Demo

Combine two queries with different 'FROM' tables but similar 'JOIN' tables

I have two queries that I'm trying to combine into one result set.
Query 1:
SELECT t1.evalID, t2.[Order], COUNT(t2.StepID) AS 'Total Categories'
FROM Evals t1
JOIN Steps t2 ON t1.TemplateID = t2.TemplateID
JOIN GradingCats t3 ON t2.StepID = t3.StepID
GROUP BY t1.EvalID, t2.[Order]
ORDER BY t2.[Order]
Query 2:
SELECT t4.EvaluatorID, t6.StepID, t6.[Order], COUNT(t4.Grade) AS 'Grades Entered'
FROM Grading t4
JOIN GradingCats t5 ON t4.GradingCatID = t5.GradingCatID
JOIN Steps t6 ON t5.StepID = t6.StepID
GROUP BY t6.StepID, t4.EvaluatorID, t6.[Order]
My end goal is to locate which steps of an evaluation have missing grades.
edit (sample data):
Query #1
|---------------------|------------------|---------------------|
| evalID | Order | Total Categories |
|---------------------|------------------|---------------------|
| 81 | 01.00 | 17 |
|---------------------|------------------|---------------------|
| 81 | 02.00 | 17 |
|---------------------|------------------|---------------------|
| 81 | 03.00 | 17 |
|---------------------|------------------|---------------------|
Query #2
|---------------------|------------------|---------------------|------------------|
| evaluatorID | Step | Order | Grades Entered |
|---------------------|------------------|---------------------|------------------|
| 1178 | 609 | 01.00 | 2 |
|---------------------|------------------|---------------------|------------------|
| 1178 | 615 | 02.00 | 3 |
|---------------------|------------------|---------------------|------------------|
| 9441 | 609 | 01.00 | 17 |
|---------------------|------------------|---------------------|------------------|
| 9441 | 609 | 02.00 | 17 |
|---------------------|------------------|---------------------|------------------|
| 9441 | 609 | 03.00 | 17 |
|---------------------|------------------|---------------------|------------------|
Starting with the first query which shows all the steps associated with an EVAL, you can LEFT OUTER JOIN the second query, and the steps that are NULL on the right side of the query will be the ones that are missing grades.
In order to do this, there must be some way in your tables to link Grading to Evals. This column is not evident from the code you posted, but I will assume it is there. Maybe it's through GradingCats.
In shortened psuedo-code, just to show what I mean:
SELECT ...
FROM Evals e
INNER JOIN Steps s ON e.TemplateID = s.TemplateID
LEFT OUTER JOIN Grading g ON g.EvalID = e.EvalID --use whatever means you have to show which Eval a Grade is from
LEFT OUTER JOIN Steps gs ON {join to Grading through GradingCats as in your second query}
WHERE gs.StepID IS NULL
In analyzing the result of this query, all the Steps of every Eval will be in s.StepID, and when the same row has a NULL for gs.StepID, that means that step did not get a grade.
Note that you won't want to do any GROUP BY in this query, since you want a row-level analysis.
A coworker (with more knowledge of the data than I) slightly modified my query:
SELECT query1.stepID, Categories, Graded
FROM
(
SELECT rs.stepid, COUNT(c.category) AS 'Categories'
FROM Evals e
JOIN RunScriptSteps rs ON e.TemplateID = rs.TemplateID
JOIN GradingCats c ON rs.StepID = c.StepID
WHERE EvalID = *(someNumber)*
GROUP BY rs.stepid
)AS query1
LEFT JOIN
(
SELECT s.StepID, COUNT(Grade) AS 'Graded'
FROM Grading g
JOIN GradingCats c ON g.GradingCatID = c.GradingCatID
JOIN Steps s ON c.StepID = s.StepID
WHERE EvalID = *(someNumber)*
GROUP BY s.stepid
) AS query2
ON query1.stepid = query2.stepid
ORDER BY stepid ASC

Update All other Records Based on a single record

I have a table with a million records. I need to update some columns which are null based on the existing 'not null' records of a particular id based columns. I've tried with one query, it seems to be working fine but I don't have confidence in it that it will be able to update all those 1 million records exactly the way I need. I'm providing you some sample data how my table looks like.Any help will be appreciated
SELECT * INTO #TEST FROM (
SELECT 1 AS EMP_ID,10 AS DEPT_ID,15 AS ITEM_NBR ,NULL AS AMOUNT,NULL AS ITEM_NME
UNION ALL
SELECT 1,20,16,500,'ABCD'
UNION ALL
SELECT 1,30,17,NULL,NULL
UNION ALL
SELECT 2,10,15,1000,'XYZ'
UNION ALL
SELECT 2,30,16,NULL,NULL
UNION ALL
SELECT 2,40,17,NULL,NULL
) AS A
Sample data:
+--------+---------+----------+--------+----------+
| EMP_ID | DEPT_ID | ITEM_NBR | AMOUNT | ITEM_NME |
+--------+---------+----------+--------+----------+
| 1 | 10 | 15 | NULL | NULL |
| 1 | 20 | 16 | 500 | ABCD |
| 1 | 30 | 17 | NULL | NULL |
| 2 | 10 | 15 | 1000 | XYZ |
| 2 | 30 | 16 | NULL | NULL |
| 2 | 40 | 17 | NULL | NULL |
+--------+---------+----------+--------+----------+
Expected result:
+--------+---------+----------+--------+----------+
| EMP_ID | DEPT_ID | ITEM_NBR | AMOUNT | ITEM_NME |
+--------+---------+----------+--------+----------+
| 1 | 10 | 15 | 500 | ABCD |
| 1 | 20 | 16 | 500 | ABCD |
| 1 | 30 | 17 | 500 | ABCD |
| 2 | 10 | 15 | 1000 | XYZ |
| 2 | 30 | 16 | 1000 | XYZ |
| 2 | 40 | 17 | 1000 | XYZ |
+--------+---------+----------+--------+----------+
I tried this but I'm unable to conclude whether it is updating all the 1 million records properly.
SELECT * FROM #TEST T
inner JOIN #TEST T1 ON T1.EMP_ID=T.EMP_ID
WHERE T1.AMOUNT IS NOT NULL
UPDATE T SET AMOUNT=T1.AMOUNT
FROM #TEST T
inner JOIN #TEST T1 ON T1.EMP_ID=T.EMP_ID
WHERE T1.AMOUNT IS not NULL
I have used UPDATE using inner join
UPDATE T
SET T.AMOUNT = X.AMT,T.ITEM_NME=X.I_N
FROM #TEST T
JOIN
(SELECT EMP_ID,MAX(AMOUNT) AS AMT,MAX(ITEM_NME) AS I_N
FROM #TEST
GROUP BY EMP_ID) X ON X.EMP_ID = T.EMP_ID
SELECT * into #Test1
FROM #TEST
WHERE AMOUNT IS NOT NULL
For records validation run this query first
SELECT T.AMOUNT, T1.AMOUNT, T1.EMP_ID,T1.EMP_ID
FROM #TEST T
inner JOIN #TEST1 T1 ON T1.EMP_ID=T.EMP_ID
WHERE T.AMOUNT IS NULL
Begin Trans
UPDATE T
SET T.AMOUNT=T1.AMOUNT, T.ITEM_NME= = T1.ITEM_NME
FROM #TEST T
inner JOIN #TEST1 T1 ON T1.EMP_ID=T.EMP_ID
WHERE T.AMOUNT IS NULL
rollback
SELECT EMP_ID,MAX(AMOUNT) as AMOUNT MAX(ITEM_NAME) as ITEM_NAME
INTO #t
FROM #TEST
GROUP BY EMP_ID
UPDATE t SET t.AMOUNT = t1.AMOUNT, t.ITEM_NAME = t1.ITEM_NAME
FROM #TEST t INNER JOIN #t t1
ON t.emp_id = t1.emp_id
WHERE t.AMOUNT IS NULL and t.ITEM_NAME IS NULL
Use MAX aggregate function to get amount and item name for each employee and then replace null values of amount and item name with those values. For validation use COUNT function to calculate the number of rows with values of amount and item name as null. If the number of rows is zero then table is updated correctly

TSQL pivot issue

Hello I have a temp table (#tempResult) that contains results like the following...
-----------------------------------------
| DrugAliasID | Dosage1 | Unit1 | rowID |
-----------------------------------------
| 322 | 10 | MG | 1 |
| 322 | 50 | ML | 2 |
| 441 | 20 | ML | 3 |
| 443 | 15 | ML | 4 |
-----------------------------------------
I'm looking to get the results to be like the following, pivoting the rows that have the same DrugAliasID.
--------------------------------------------------
| DrugAliasID | Dosage1 | Unit1 | Dosage2 | Unit2 |
--------------------------------------------------
| 322 | 10 | MG | 50 | ML |
| 441 | 20 | ML | NULL | NULL |
| 443 | 15 | ML | NULL | NULL |
--------------------------------------------------
So far I have a solution that isn't using pivot. I'm not too good with pivot and was wondering if anyone knew how to use it in this scenario. Or solve it some other way. Thanks
SELECT
tr.drugAliasID,
MIN(trmin.dosage1) AS dosage1,
MIN(trmin.unit1) AS unit1,
MIN(trmax.dosage1) AS dosage2,
MIN(trmax.unit1) AS unit2
FROM
#tempResult tr
JOIN
#tempResult trmin ON trmin.RowID = tr.rowid AND trmin.drugAliasID = tr.drugAliasID
JOIN
#tempResult trmax ON trmax.RowID = tr.rowid AND trmax.drugAliasID = tr.drugAliasID
JOIN
(SELECT
MIN(RowID) AS rowid,
drugAliasID
FROM
#tempResult
GROUP BY
drugAliasID) tr1 ON tr1.rowid = trmin.RowID
JOIN
(SELECT
MAX(RowID) AS rowid,
drugAliasID
FROM
#tempResult
GROUP BY
drugAliasID) tr2 ON tr2.rowid = tr.RowID
GROUP BY
tr.drugAliasID
HAVING
count(tr.drugAliasID) > 1
Assuming your version of SQL Server supports the use of CTEs, you can simplify your query thus:
;with cte as
(select *, row_number() over (partition by drugaliasid order by rowid) rn
from #tempResult
)
select c.drugaliasid, c.dosage1, c.unit1, c2.dosage1 as dosage2, c2.unit1 as unit2
from cte c
left join cte c2 on c.drugaliasid = c2.drugaliasid and c.rn = 1 and c2.rn = 2
where c.rn = 1
Demo
This will give you the desired result, without having to use the pivot keyword.

Retrieving the most recent records within a query

I have the following tables:
tblPerson:
PersonID | Name
---------------------
1 | John Smith
2 | Jane Doe
3 | David Hoshi
tblLocation:
LocationID | Timestamp | PersonID | X | Y | Z | More Columns...
---------------------------------------------------------------
40 | Jan. 1st | 3 | 0 | 0 | 0 | More Info...
41 | Jan. 2nd | 1 | 1 | 1 | 0 | More Info...
42 | Jan. 2nd | 3 | 2 | 2 | 2 | More Info...
43 | Jan. 3rd | 3 | 4 | 4 | 4 | More Info...
44 | Jan. 5th | 2 | 0 | 0 | 0 | More Info...
I can produce an SQL query that gets the Location records for each Person like so:
SELECT LocationID, Timestamp, Name, X, Y, Z
FROM tblLocation
JOIN tblPerson
ON tblLocation.PersonID = tblPerson.PersonID;
to produce the following:
LocationID | Timestamp | Name | X | Y | Z |
--------------------------------------------------
40 | Jan. 1st | David Hoshi | 0 | 0 | 0 |
41 | Jan. 2nd | John Smith | 1 | 1 | 0 |
42 | Jan. 2nd | David Hoshi | 2 | 2 | 2 |
43 | Jan. 3rd | David Hoshi | 4 | 4 | 4 |
44 | Jan. 5th | Jane Doe | 0 | 0 | 0 |
My issue is that we're only concerned with the most recent Location record. As such, we're only really interested in the following Rows: LocationID 41, 43, and 44.
The question is: How can we query these tables to give us the most recent data on a per-person basis? What special grouping needs to happen to produce the desired result?
MySQL doesn't have ranking/analytical/windowing functionality.
SELECT tl.locationid, tl.timestamp, tp.name, X, Y, Z
FROM tblPerson tp
JOIN tblLocation tl ON tl.personid = tp.personid
JOIN (SELECT t.personid,
MAX(t.timestamp) AS max_date
FROM tblLocation t
GROUP BY t.personid) x ON x.personid = tl.personid
AND x.max_date = tl.timestamp
SQL Server 2005+ and Oracle 9i+ support analytics, so you could use:
SELECT x.locationid, x.timestamp, x.name, x.X, x.Y, x.Z
FROM (SELECT tl.locationid, tl.timestamp, tp.name, X, Y, Z,
ROW_NUMBER() OVER (PARTITION BY tp.name ORDER BY tl.timestamp DESC) AS rank
FROM tblPerson tp
JOIN tblLocation tl ON tl.personid = tp.personid) x
WHERE x.rank = 1
Using a variable to get same as ROW_NUMBER functionality on MySQL:
SELECT x.locationid, x.timestamp, x.name, x.X, x.Y, x.Z
FROM (SELECT tl.locationid, tl.timestamp, tp.name, X, Y, Z,
CASE
WHEN #name != t.name THEN
#rownum := 1
ELSE #rownum := #rownum + 1
END AS rank,
#name := tp.name
FROM tblLocation tl
JOIN tblPerson tp ON tp.personid = tl.personid
JOIN (SELECT #rownum := NULL, #name := '') r
ORDER BY tp.name, tl.timestamp DESC) x
WHERE x.rank = 1
As #Mark Byers mentions, this problem comes up frequently on Stack Overflow.
Here's the solution I most frequently recommend, given your tables:
SELECT p.*, l1.*
FROM tblPerson p
JOIN tblLocation l1 ON p.PersonID = l1.PersonID
LEFT OUTER JOIN tblLocation l2 ON p.PersonID = l2.PersonID AND
(l1.timestamp < l2.timestamp OR l1.timestamp = l2.timestamp AND l1.LocationId < l2.LocationId)
WHERE l2.LocationID IS NULL;
To see other examples, follow the tag greatest-n-per-group, which I added to your question.
This is a classic 'max per group' question that comes up on Stack Overflow almost every day. There are many ways to solve it and you can find example solutions by searching Stack Overflow. Here is one way that you can do it in MySQL:
SELECT
location.LocationId,
location.Timestamp,
person.Name,
location.X,
location.Y,
location.Z
FROM (
SELECT
LocationID,
#rn := CASE WHEN #prev_PersonID = PersonID
THEN #rn + 1
ELSE 1
END AS rn,
#prev_PersonID := PersonID
FROM (SELECT #prev_PersonID := NULL) vars, tblLocation
ORDER BY PersonID, Timestamp DESC
) T1
JOIN tblLocation location ON location.LocationID = T1.LocationId
JOIN tblPerson person ON person.PersonID = location.PersonID
WHERE rn = 1

Resources