Merge rows by adding the deviating value as new columns - sql

I want to output for each person that has filled out a specific questionnaire their personal data as well as the data from the specific questionnaire/assessment. The values are stored in the table criteriaDF.
The result of my current query:
KlientId
Name1
Name2
CriteriaName
Result
335
Name1Person1
Name2Person1
IF1
Yes
335
Name1Person1
Name2Person1
IF2
Yes
335
Name1Person1
Name2Person1
IF3
No
336
Name1Person2
Name2Person2
IF1
Yes
336
Name1Person2
Name2Person2
IF2
Yes
336
Name1Person2
Name2Person2
IF3
No
What I want to have:
KlientId
Name1
Name2
IF1
IF2
IF3
335
Person1Name1
Person1Name2
Yes
Yes
No
336
Person2Name1
Person2Name2
Yes
No
No
So the criterias should get their own columns and the rows referencing the same person should merge into one based on the same KlientId.
The query I used to get my current result uses a few joins in order to get from person to client, to process to assessment to criteria, where the CriteriaName and Result lies. The other tables are just used with their foreign keys to get to these values "IF1: Yes" etc.
SELECT Client.Id, Person.Name1, Person.Name2, Person.Birthday, CriteriaDF.CriteriaName, CriteriaDF.Result
FROM Client
INNER JOIN Person ON Client.PersonId=Person.Id
INNER JOIN Process ON Client.Id=Process.ClientId
INNER JOIN AssessmentDF ON Process.Id=AssessmentDF.ProCessId
INNER JOIN CriteriaDF ON AssessmentDF.Id=CriteriaDF.AssessmentDfId
WHERE AssessmentDF.Name='RightAssessmentName' AND AssessmentDF.Date > DATEADD(day, -180, GETDATE())
Edit: Query using aliases:
SELECT t1.Id, t2.Name1, t2.Name2, t2.Birthday, t5.CriteriaName, t5.Result
FROM Client AS t1
INNER JOIN Person AS t2 ON t1.PersonId=t2.Id
INNER JOIN Process AS t3 ON t1.Id=t3.ClientId
INNER JOIN AssessmentDF AS t4 ON t3.Id=t4.ProcessId
INNER JOIN CriteriaDF AS t5 ON t4.Id=t5.AssessmentDfId
WHERE t4.Bezeichnung='RightAssessmentName' AND t4.Date > DATEADD(day, -180, GETDATE())
My main question is how to convert the tuples CriteriaName & Result to a new column for each unique CriteriaName and Fill the cell with the Result.
I dont think they're important, but here are all minimal tables that are used to get from the person to the criterias (Ids might not fit perfectly to the result & what I wanted to have, just to understand how the data is stored):
Table Person:
Id
Name1
Name2
2766
Person1Name1
Person1Name2
2767
Person2Name2
Person2Name2
2768
Person3Name2
Person3Name2
Table Klient:
Id
PersonId
1
2766
335
2767
336
2768
Table Process:
Id
KlientId
2485
335
2515
336
2519
336
Table AssessmentDF
Id
ProcessId
Date
Name
43
2485
2022-04-18
RightAssessmentName
44
2515
2022-05-18
RightAssessmentName
45
2519
2022-06-18
RightAssessmentName
Table CriteriaDF:
In reality there is IF1-If19
Id
AssessmentDfId
CriteriaName
ProcessId
Result
551
43
IF1
2485
Yes
552
43
IF2
2485
Yes
553
43
IF3
2485
No
554
44
IF1
2515
Yes
555
44
IF2
2515
Yes
556
44
IF3
2515
No
557
45
IF1
2519
Yes
558
45
IF2
2519
No
559
45
IF3
2519
No

Related

Is there is way to get SUM() of column without GROUPING by joining multiple tables in SQL Server

I am getting SUM() of amount in CrowdfundedUser table by GROUP BY CrowdfundID but difficult to get SUM() because all columns are unique.
Crowdfund:
CrowdfundID
GoalAmount
StartedDate
9
10000
09/02/2022
5
20000
10/02/2022
55
350000
11/02/2022
444
541256
12/02/2022
54
78458
13/02/2022
CrowdfundedUser:
ID
User ID
CrowdfundID
Amount
744
12214
9
1000
745
4124
5
8422
746
12214
55
784
747
12214
444
874
748
64554
54
652
CrowdfundiPaymentTransaction:
CrowdfundedUserID
Invoice
Amount
PaymentDate
744
RA45A14124
1000
09/02/2022
745
RA45A12412
8422
10/02/2022
746
RA45U14789
784
11/02/2022
747
RA45F12457
874
12/02/2022
748
RA45M00124
652
13/02/2022
My query :
SELECT
c.CrowdfundID,
SUM(cu.Amount),
SUM(cpt..Amount)
FROM
Crowdfund c
INNER JOIN
CrowdfundedUser cu ON c.CrowdfundID = cu.CrowdfundID
INNER JOIN
CrowdfundiPaymentTransaction cpt ON cu.ID = cpt.CrowdfundedUserID
GROUP BY
c.CrowdfundID
SELECT c.CrowdfundID,
SUM(cu.Amount) OVER (
ORDER BY c.CrowdfundID) Amount,
SUM(cpt..Amount) OVER (
ORDER BY c.CrowdfundID) CptAmount
FROM Crowdfund c
INNER JOIN CrowdfundedUser cu ON c.CrowdfundID = cu.CrowdfundID
INNER JOIN CrowdfundiPaymentTransaction cpt ON cu.ID = cpt.CrowdfundedUserID

Using multiple and interdepended CROSS-APPLY across multiple tables

How can I use either CROSS APPLY (or INNER JOIN) to get data from one table based on the values in other tables?
I.e. I have the following tables:
Table Descriptions:
ProdID
Description
TrackNum
361
Test 1
499
388
Test 2
003
004
5599
238
Test 3
499
361
Test 10
555
004
Test 40
555
Table Products:
ProdID
ProductName
Price
361
P1
5.00
388
P2
5.00
004
P3
12.00
238
P4
6.00
515
P5
7.00
636
P6
7.00
775
P7
7.00
858
P8
8.00
Table Invoices:
ProdID
TrackNum
InvoiceID
361
499
718
388
199
718
004
499
718
238
499
718
361
555
333
004
555
444
361
111
444
388
222
333
616
116
565
717
116
565
361
003
221
388
003
221
004
5599
728
What I need my query to do is to:
Go into Invoices table first, and get only records that matches specified InvoiceID and TrackNum;
Then go into Products table and get only rows that have matches on ProdID between the data I pulled out in Step #1 and the data existis in the Products table.
Then finally get all columns from the Descriptions table, but only for the rows which I got in the Step #2 and which matches on ProdID.
What I need to have at the end is something like this (if I get more columns that is fine, but I do not want to get more rows):
ProdID
Description
TrackNum
361
Test 1
499
004
5599
238
Test 3
499
I have following query (and I have tried using INNER JOIN and CROSS APPLY) - but it returns me more rows than I need:
SELECT * FROM [Descriptions] AS [DES]
CROSS APPLY
(
select * from [Invoices] AS [INV] where [INV].[TrackNum] = '499' AND [INV].[InvoiceID] = '718'
) [INV]
CROSS APPLY
(
select * from [Products] AS [GP]
WHERE [GP].[ProdID] = [INV].[ProdID]
) [GP2]
WHERE
[DES].[ProdID] = [GP2].[ProdID]
order by [DES].[ProdID] asc
SELECT
*
FROM
invoices AS i
LEFT JOIN
descriptions AS d
ON d.prodid = i.prodid
AND d.tracknum = i.tracknum -- you don't have this, but I think it's required.
LEFT JOIN
products AS p
ON p.prodid = i.prodid
WHERE
i.invoiceid = 718
AND i.tracknum = 499
ORDER BY
i.prodid
One thing that concerns me is that both the invoices and descriptions have a column named tracknum, but your query and expected data indicate that you don't want to include that in the join? That's very confusing and either a poor column name, or a mistake in your query and example results.
Based on what you describe you want the following, start with your Invoices table and a where clause to get the right rows, then join on Products and Descriptions.
I'm also guessing that you want to match the Description on TrackNum? Since it appears you have a unique Description per ProdId/TrackNum combination.
select [INV].[ProdID], [DES].[Description], [INV].[TrackNum]
from [Invoices] as [INV]
inner join [Products] as [GP] on [GP].[ProdID] = [INV].[ProdID]
inner join [Descriptions] on [DES].[ProdID] = [GP].[ProdID] and [DES].[TrackNum] = [INV].[TrackNum]
where [INV].[TrackNum] = '499' AND [INV].[InvoiceID] = '718'
order by [DES].[ProdID] asc;
Note: You normally only use a 'CROSS APPLY' for queries where you want to run/evaluate something per row in your main table.
In this case the Inner Join is sufficient. You don't need to use Cross Apply

SQL query to allow NULL for a highest_bid column when no bid has been placed yet

For school I need to make a function on an auction website. For this I need to join a couple of tables in a VIEW. This worked just fine, until I needed to add a filter for price range. Seems easy enough but the query result needs to allow a NULL when no bid has been placed.
The Statement for the View:
SELECT I.itemID, I.title, I.startPrice, B.highestBid, Cfi.category, I.endDate
FROM dbo.Items AS I INNER JOIN dbo.category_for_item AS Cfi ON V.itemID = Vir.itemID
LEFT OUTER JOIN dbo.Bid AS B ON V.itemID = B.itemID
This would get the following Table:
itemID title startPrice highestBid category endDate
1 1234 Alfa 25 26 PC 2018-09-22
2 1234 Alfa 25 NULL PC 2018-09-22
3 5678 Bravo 9 20 Console 2018-07-03
4 5678 Bravo 9 15 Console 2018-07-03
5 5678 Bravo 9 NULL Console 2018-07-03
6 9876 Charlie 84 100 Stamps 2018-06-14
7 9876 Charlie 84 90 Stamps 2018-06-14
8 9876 Charlie 84 85 Stamps 2018-06-14
9 9876 Charlie 84 NULL Stamps 2018-06-14
10 1470 Delta 98 100 Fashion 2018-06-15
11 1470 Delta 98 99 Fashion 2018-06-15
12 1470 Delta 98 NULL Fashion 2018-06-15
13 9631 Echo 56 65 Cars 2018-06-25
14 9631 Echo 56 NULL Cars 2018-06-25
15 7856 Foxtrot 98 NULL Dolls 2018-12-26
After looking around for answers I got a query for joining the VIEW on itself with only showing the highest bid instead of all bids:
SELECT VW.itemID, VW.title, VW.startPrice, VW.highestBid, VW.category, VW.endDate
FROM VW_SEARCH AS VW
INNER JOIN (SELECT itemID, MAX(highestBid) AS MaxBid
FROM VW_SEARCH
GROUP BY itemID) VJ
ON VW.itemID = VJ.itemID AND VW.highestBid = VJ.MaxBid
This gave the next results:
itemID title startPrice highestBid category endDate
1 1234 Alfa 25 26 PC 2018-09-22
2 5678 Bravo 9 20 Console 2018-07-03
3 9876 Charlie 84 85 Stamps 2018-06-14
4 1470 Delta 98 100 Fashion 2018-06-15
5 9631 Echo 56 65 Cars 2018-06-25
As I expected the result only showed the items with at least one bid on them. I tried added one extra condition on the subQuery and Joining RIGHT OUTER to make sure I would not get doubles of an itemID.
SELECT VW.itemID, VW.title, VW.startPrice, VW.highestBid, VW.category, VW.endDate
FROM VW_SEARCH AS VW
RIGHT OUTER JOIN (SELECT itemID, MAX(highestBid) AS MaxBid
FROM VW_SEARCH
WHERE highestBid > 0 OR highestBid IS NULL
GROUP BY itemID) VJ
ON VW.itemID = VJ.itemID AND VW.highestBid = VJ.MaxBid
This gave the following results (did not add result 5 - 1199 because it is all the same as result 4, this would happen in the actual table not the example table from above):
itemID title startPrice highestBid category endDate
1 1234 Alfa 25 26 PC 2018-09-22
2 5678 Bravo 9 20 Console 2018-07-03
3 9876 Charlie 84 85 Stamps 2018-06-14
4 NULL NULL NULL NULL NULL NULL
1200 1470 Delta 98 100 Fashion 2018-06-15
1201 9631 Echo 56 65 Cars 2018-06-25
While this is technicly allowing a NULL in the colums I need to get a result in the likes of :
itemID title startPrice highestBid catgory endDate
1 1234 Alfa 25 26 PC 2018-09-22
2 5678 Bravo 9 20 Console 2018-07-03
3 9876 Charlie 84 85 Stamps 2018-06-14
4 1470 Delta 98 100 Fashion 2018-06-15
5 9631 Echo 56 65 Cars 2018-06-25
6 7856 Foxtrot 98 NULL Dolls 2018-12-26
How do I get the desired result, or is it just impossible?
Also if the query could be written better, please say so.
Thanks in advance.
Solve the problem using a left join:
SELECT VW.itemID, VW.title, VW.startPrice, VW.highestBid, VW.category, VW.endDate
FROM VW_SEARCH VW LEFT JOIN
(SELECT itemID, MAX(highestBid) AS MaxBid
FROM VW_SEARCH
GROUP BY itemID
) VJ
ON VW.itemID = VJ.itemID AND VW.highestBid = VJ.MaxBid;
Or, use the ANSI-standard ROW_NUMBER() function:
select vw.*
from (select vw.*,
row_number() over (partition by itemID
order by highestBid nulls last
) as seqnum
from vw_search vw
) vw
where seqnum = 1;
This guarantees one row per item.
Note: Not all databases support NULLS LAST. This may not even be necessary, but you can also implement it using a case expression.
Can you give the definition of the view at least? Maybe the table definition too.
I would go only with subquery as because identity column :
select vw.*
from vw_search vw
where id = (select vm1.id
from vw_search vm1
where vm.itemID = vw1.itemID and vm1.highestBid is not null
order by vm1.highestBid desc
limit 1
);
However, some DBMS has not support LIMIT clause such as SQL Server if so, then you can use TOP clause instead.

UPDATE FROM in SQL with matching key

How can I UPDATE FROM in SQL? I want to copy the FamilyID from my FamilyGuardian table:
dbo.FamilyGuardian
PupilID FamilyID GuardianID
---------------------------------
1 100002 555
2 100003 556
3 100004 557
4 100005 558
5 100006 559
6 100007 1146
7 100008 561
8 100009 562
9 100010 563
10 100011 564
Into the Guardians table where GuardianID is equal.
dbo.Guardians
GuardianID Name FamilyID
---------------------------------
555 Smith NULL
556 Patel NULL
557 Andrews NULL
558 Brown NULL
559 Abdul NULL
dbo.Guardians after INSERT FROM
GuardianID Name FamilyID
--------------------------------
555 Smith 100002
556 Patel 100003
557 Andrews 100004
558 Brown 100005
559 Abdul 100006
you have to use an UPDATE query
UPDATE T1
SET T1.FamiltyID = T2.FamilyID
FROM dbo.Guardians AS T1 INNER JOIN dbo.FamilyGuardian AS T2
ON T1.GuardianID = T2.GuardianID
There is no such thing as INSERT FROM - you're looking for UPDATE, not INSERT:
Update G
Set FamilyId = F.FamilyId
From FamilyGuardian F
Join Guardian G On G.GuardianId = F.GuardianId
You might use an updateable CTE for this
WITH UpdateableCTE AS
(
SELECT g.FamilyID
,fg.FamilyID AS newValue
FROM dbo.Guardians AS g
INNER JOIN dbo.FamilyGuardian AS fg ON g.GuardianID=fg.GuardianID
)
UPDATE UpdateableCTE SET FamilyID=newValue;
But you must be aware, that this design is - uhm - weak...
What would happen if one Guard is responsible for more than one family? What happens, if the information on one side changes, but the other side does not change?
I think, this should not get into a physically defined column at all. This should be taken with a JOIN whenever you read this data.
you mean an UPDATE, not an INSERT, right?
In that case you should use an structue like:
UPDATE dbo.Guardians
SET FamilyID = (SELECT DISTINCT FamilyID FROM dbo.FamilyGuardian fg WHERE fg.GuardianID = GuardianID )
Notice that the DISTINCT is only necessary when there are more than 1 familyId per guardianId.

SQL Query pivot approach assistance

i am really struggling with this pivot and hoped reaching out for help and enlightenment might help.
Say i have the following table....
Table A
type actId date rowSort order value value_char colName
------------------------------------------------------------------------------------
checking 1003 2011-12-31 2 1 44 44 Amount
checking 1003 2011-12-31 2 2 55 55 Interest
checking 1003 2011-12-31 2 3 66 66 Change
checking 1003 2011-12-31 2 4 77 77 Target
checking 1003 2011-12-31 2 5 88 88 Spread
savings 23456 2011-12-31 1 1 999 999 Amount
savings 23456 2011-12-31 1 2 888 888 Interest
savings 23456 2011-12-31 1 3 777 777 Change
savings 23456 2011-12-31 1 4 666 666 Target
savings 23456 2011-12-31 1 5 555 555 Spread
And i want to transpose to table b
checking chkId date rowSort order chkvalue chkValchar colName savings savId savVal savValChar
-------------------------------------------------------------------------------------------------------------------
checking 1003 2011-12-31 2 1 44 44 Amount savings 23456 999 999
checking 1003 2011-12-31 2 2 55 55 Interest savings 23456 888 888
checking 1003 2011-12-31 2 3 66 66 Change savings 23456 777 777
checking 1003 2011-12-31 2 4 77 77 Target savings 23456 666 666
checking 1003 2011-12-31 2 5 88 88 Spread savings 23456 555 555
I can admit this is beyond my skills at the moment.
I believe i need to do a pivot on this table, using the rowSort (identify savings vs checking) along with ordering using the order column. This maybe wrong and that is why i am here.
Is a pivot the right way to go? Am i right to assume my pivot is to use the aggregate max(rowSort)?
Assuming rowSort from `checking equal to rowSort+1 from savings and the rows link though field value, this should do it:
SELECT DISTINCT
a.type as checking,
a.actId as chkId,
a.date,
a.rowSort+1,
a.order,
a.value as chkvalue,
a.value_char as chkValchar,
a.colName,
b.type as 'savings',
a.actId as savId,
b.value as savVal,
b.value_char as savValChar
FROM tablea a
INNER JOIN tablea b ON b.rowSort = a.rowSort+1 and b.value = a.value
Based on the requirements you presented, you will not use a PIVOT for this query, you will want to JOIN your table to itself. The query below should give you the records that you want without having to use a DISTINCT
select c.type as checking
, c.actId as chkid
, c.date
, c.rowsort
, c.[order]
, c.value as chkvalue
, c.value_char as chkValchar
, c.colName
, s.type as savings
, s.actId as savId
, s.value as savVal
, s.value_char as savValchar
from t1 c
inner join t1 s
on c.rowsort = s.rowsort + 1
and c.[order] = s.[order]
See SQL Fiddle with Demo