SQL Server: UPDATE after pivot (unpivot needed?) - sql

Two days ago I asked how to use pivot in SQL to transpose a table.
With your help I got it right, here the solution: SQL server: Transpose Rows to Columns (n:m relationship)
I'm running SQL Server 2012, with an OBDC-connection, programming in C#.
So now I have:
| mitID | Name | FamName | DOB | abtIDref | HV | PEV | Drive | Nex |
|-------|--------|---------|------------|----------|---------------|------------|------------|------------|
| 1 | Frank | Sinatra | 12.12.1915 | 1 | **30.5.2016** | | 05.06.2015 | 02.11.2015 |
| 2 | Robert | Downey | 4.4.1965 | 2 | 27.7.2014 | 01.01.2016 | 20.01.2015 | |
I get this with following SQL statement:
DECLARE #sql AS NVARCHAR(MAX)
DECLARE #cols AS NVARCHAR(MAX)
SELECT #cols = ISNULL(#cols + ',', '') + QUOTENAME(Descr)
FROM Faehigkeiten ORDER BY faeID
SET #sql = N'
SELECT mitID, Name, FamName, DOB, abtIDref, ' + #cols + '
FROM(
SELECT mitID, Name, FamName, DOB, abtIDref, [when], descr
FROM Mitarbeiter m
JOIN[Link - List] l ON m.mitID = l.mitIDref
JOIN Faehigkeiten f ON f.faeID = l.feaIDref
) a
PIVOT(MAX([when]) FOR descr IN(' + #cols + ')) p'
EXEC sp_executesql #sql
But when I enter a new date (shown in asterisk above), or change a name of a member, how do I save the data?
I thought about unpivot, but I have no idea how to use it in my case.
My solution would be to loop through all the rows and compare it to existing entries ... but that could take a long time with lots of members.
Can you point me in some direction, wether it is possible to UPDATE this an easy way? I only need UPDATE no INSERT since new rows are forbidden.
Right now I have no class for my members. I load my DataGridView in C# with the following code:
BindingSource bsMember = new BindingSource();
bsMember.DataSource = DBMember.Get();
dgv.DataSource = bsMember;
The DBMember.Get() returns a DataTable made with the above shown SQL code.
Sorry, I have no approach except my loop through each row :/
EDIT: The provided duplicate is hard for me to follow because of the JOIN in my SQL-code. Can i JOIN while updating?
Please consider helping me directly.

Related

Row values as column headings

How can I make the following sql result:
Be rendered in a result set like this
| ID | Chain Size | Length | Hook Type | Shortening Grab |
|-----|-----------------|---------|------------|-----------------|
| 163 | 7mm (1.5 tonne) | 1 metre | C Hook | Yes |
| 226 | 7mm (1.5 tonne) | 1 metre | C Hook | No |
| 247 | 7mm (1.5 tonne) | 1 metre | Latch Hook | No |
I know that the values in columns 2,4,6 and 8 (which I want to be headers) will be the same across all rows (but different depending on the initial query).
I believe the approach for what I want is through the use of PIVOT but really struggling to get the desired result.
Thanks
Assuming your source data actually looks like this:
Static Pivot
Select *
From YourTable
Pivot (max(attributeValue) For [attributeName] in ([Chain Size],[Length],[Hook Type],[Shortening Grab]) ) p
Returns
Dynamic Approach
Declare #SQL varchar(max) = Stuff((Select Distinct ',' + QuoteName([attributeName]) From Yourtable Order by 1 For XML Path('')),1,1,'')
Select #SQL = '
Select *
From YourTable
Pivot (max(attributeValue) For [attributeName] in (' + #SQL + ') ) p'
Exec(#SQL);
Returns
Notice, without an item for column sequence, you'll see that they are alphabetical.

Dynamic Pivot table error

I am currently having trouble getting the following to work. I have a table, 'tbl_View', that exists in my database that has the column names of another table held in its rows (users are able to choose specific columns that they wish to display and their list of their choices is kept here).
I then want to create a view that will bring only each users specific column choices from the target table 'AREG' and would like to do so dynamically if possible.
Table Samples - AREG Column names:
ID | Text_1 | Text_2 | Text_3 | Text_4 etc...
tbl_View Data:
ViewID | lblValue | fldName | showYN | sOrder
10 | EANPR | YesNo8 | True | 80
10 | INSP_DATE| Date_1 | True | 81
10 | KG_DATE | Date_2 | True | 82
11 | Life | Num_13 | True | 35
11 | Area | Dimension_1 | True | 69
11 | Length | Dimension_2 | True | 70
11 | EANPR | YesNo8 | True | 80
12 | TRAVEL | Text_1 | True | 1
12 | SPILLLVE | Text_2 | True | 2
12 | SLOPE_PCT| Text_3 | True | 3
14 |Project_Name | Text_1 | True | 1
14 | Project Description | Text_2 | 2
code is:
DECLARE #DynamicPivotQuery AS NVARCHAR(MAX)
DECLARE #ColumnName AS NVARCHAR(MAX)
--Get distinct values of the PIVOT Column
SELECT #ColumnName= ISNULL(#ColumnName + ',','') + QUOTENAME(fldName)
FROM (SELECT DISTINCT fldName FROM qry_ADet WHERE (Viewid=14)) AS fname
--Prepare the PIVOT query using the dynamic
SET #DynamicPivotQuery = N'SELECT ' + #ColumnName + ' FROM #AREG PIVOT ' + #ColumnName + ' AS PVTTable'
--Execute the Dynamic Pivot Query
EXEC sp_executesql #DynamicPivotQuery
I keep getting the error:
Msg 102, Level 15, State 1, Line 1
Incorrect syntax near 'Text_1'.
I have hard coded the view id into the query for this example but will use a parameter in my final query as it will be referenced by an asp.net datasource. But the error points to a different column name if you change the Viewid clause.
Please help.
Thanks
to debug a dynamic sql query, do a PRINT statement before executing it.
You will probably see that the error is from #DynamicPivotQuery
- you should be selecting from tbl_View instead of #AREG right ?
- your pivot statement is wrong. it should be something like
FROM sometable
PIVOT
(
sum( somecol)
for anothercol in ( [value1], [value2] , [value3] )
) p
too long for comment, I think the issue may stem from the column concatenation and it needs something like this:
SET #ColumnName = STUFF((SELECT distinct ',' + QUOTENAME(fldName)
FROM qry_ADet
WHERE (Viewid=14)
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')

LIKE condition with comma separated values

This may seem silly but I am working on an old system where we don't have separate table for project. We have a table something like this.
| PROJECTNAME | EMPID |
|--------------------|-------|
| casio calc new | 1 |
| calc new | 2 |
| tech | 3 |
| financial calc new | 4 |
| casio | 5 |
Now what we want is to select EmpID from the above table where ProjectName can be either casio or calc. And we have user inputs for multiple selection like casio, calc. Meaning if user inputs casio, calc it should find ProjectName LIKE '%casio%' OR '%calc%'.
I really don't have any idea how can this be possible.
SELECT * FROM ProjectDetails
WHERE ProjectName LIKE 'casio, calc';
I searched for SQL LIKE with IN but I couldn't find any solution. Do anyone have idea how can I achieve this? Or any other approach to use? I am trying this on this fiddle.
Here you go. You can create a dynamic query with the help of REPLACE function with a little bit hack. Then use EXEC function to execute that query.
DECLARE #str VARCHAR(MAX)
SELECT #str='casio,calc'
SELECT #str='LIKE ''%'+REPLACE(#str,',','%'' OR ProjectName LIKE ''%')+'%'''
EXEC('SELECT * FROM ProjectDetails WHERE
ProjectName ' + #str +'');
Output:
| PROJECTNAME | EMPID |
|--------------------|-------|
| casio calc new | 1 |
| calc new | 2 |
| financial calc new | 4 |
| casio | 5 |
SQL Fiddle Demo
Thanks to Joe G Joseph for his hint.
The solution is to use several LIKE conditions:
SELECT *
FROM ProjectDetails
WHERE ProjectName LIKE '%casio%'
OR ProjectName LIKE '%calc%';
Like doesn't except comma delimited values. You are going to have to dynamically add an or condition for each passed in value.
It has to be of the form:
ProjectName Like'%param1%' OR ProjectName Like '%param2%'
We declare #str variable and store value we want to search and using REPLACE function place like keyword for every value and assign it to main query and execute it, you can get result
DECLARE #Query VARCHAR(MAX)
DECLARE #str VARCHAR(MAX)
SELECT #str='tech,casio, calc,casio calc new'
SELECT #str='LIKE ''%'+REPLACE(#str,',','%'' OR ProjectName LIKE ''%')+'%'''
set #Query ='SELECT * FROM ProjectDetails WHERE
ProjectName ' + #str
exec( #Query)

SQL Server 2005 Pivot on Unknown Number of Columns

I am working with a set of data that looks something like the following.
StudentName | AssignmentName | Grade
---------------------------------------
StudentA | Assignment 1 | 100
StudentA | Assignment 2 | 80
StudentA | Total | 180
StudentB | Assignment 1 | 100
StudentB | Assignment 2 | 80
StudentB | Assignment 3 | 100
StudentB | Total | 280
The name and number of assignments are dynamic, I need to get results simlilar to the following.
Student | Assignment 1 | Assignment 2 | Assignment 3 | Total
--------------------------------------------------------------------
Student A | 100 | 80 | null | 180
Student B | 100 | 80 | 100 | 280
Now ideally I would like to sort the column based on a "due date" that could be included/associated with each assignment. The total should be at the end if possible (It can be calculated and removed from the query if possible.)
I know how to do it for the 3 assignments using pivot with simply naming the columns, it is trying to do it in a dynamic fashion that I haven't found a GOOD solution for yet. I am trying to do this on SQL Server 2005
EDIT
Ideally I would like to implement this WITHOUT using Dynamic SQL, as that is against the policy. If it isn't possible...then a working example with Dynamic SQL will work.
I know you said no dynamic SQL, but I don't see any way to do it in straight SQL.
If you check out my answers to similar problems at Pivot Table and Concatenate Columns and PIVOT in sql 2005
The dynamic SQL there is not vulnerable to injection, and there is no good reason to prohibit it. Another possibility (if the data is changing very infrequently) is to do code-generation - instead of dynamic SQL, the SQL is generated to a stored procedure on a regular basis.
To PIVOT this data using dynamic sql you can use the following code in SQL Server 2005+:
Create Table:
CREATE TABLE yourtable
([StudentName] varchar(8), [AssignmentName] varchar(12), [Grade] int)
;
INSERT INTO yourtable
([StudentName], [AssignmentName], [Grade])
VALUES
('StudentA', 'Assignment 1', 100),
('StudentA', 'Assignment 2', 80),
('StudentA', 'Total', 180),
('StudentB', 'Assignment 1', 100),
('StudentB', 'Assignment 2', 80),
('StudentB', 'Assignment 3', 100),
('StudentB', 'Total', 280)
;
Dynamic PIVOT:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(AssignmentName)
from yourtable
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT StudentName, ' + #cols + ' from
(
select StudentName, AssignmentName, grade
from yourtable
) x
pivot
(
min(grade)
for assignmentname in (' + #cols + ')
) p '
execute(#query)
See SQL Fiddle with Demo
The result is:
| STUDENTNAME | ASSIGNMENT 1 | ASSIGNMENT 2 | ASSIGNMENT 3 | TOTAL |
--------------------------------------------------------------------
| StudentA | 100 | 80 | (null) | 180 |
| StudentB | 100 | 80 | 100 | 280 |
The only way I've found to do this is to use dynamic SQL and put the column labels into a variable.
you could query information_schema to get the column names and types, then use the result as a subquery when you build your result set. Note you'll likely have to change the login's access a bit.
This is the same as PIVOT in sql 2005
If this data is for consumption in a report you could use a SSRS matrix. It will generate columns dynamically from result set. I've used it many times - it works quite well for dynamic crosstab reports.
Here's a good example w/ dynamic sql.
http://www.simple-talk.com/community/blogs/andras/archive/2007/09/14/37265.aspx
SELECT TrnType
INTO #Temp1
FROM
(
SELECT '[' + CAST(TransactionType AS VARCHAR(4)) + ']' AS TrnType FROM tblPaymentTransactionTypes
) AS tbl1
SELECT * FROM #Temp1
SELECT * FROM
(
SELECT FirstName + ' ' + LastName AS Patient, TransactionType, ISNULL(PostedAmount, 0) AS PostedAmount
FROM tblPaymentTransactions
INNER JOIN emr_PatientDetails ON tblPaymentTransactions.PracticeID = emr_PatientDetails.PracticeId
INNER JOIN tblPaymentTransactionDetails ON emr_PatientDetails.PatientId = tblPaymentTransactionDetails.PatientID
AND tblPaymentTransactions.TransactionID = tblPaymentTransactionDetails.TransactionID
WHERE emr_PatientDetails.PracticeID = 152
) tbl
PIVOT (SUM(PostedAmount) FOR [TransactionType] IN (SELECT * FROM #Temp1)
) AS tbl4
select studentname,[Assign1],[Assign2],[Assign3],[Total]
from
(
select studentname, assignname, grade from student
)s
pivot(sum(Grade) for assignname IN([Assign1],[Assign2],[Assign3],[Total])) as pvt

How to merge multiple rows into one row with filtering rules in SQL Server

I have a table like this:
+---------------+---------------+----------------+---------------------+
| MedicalCardId | DiagnosisType | DiagnosisOrder | Symptom |
+---------------+---------------+----------------+---------------------+
| 1 | Main | 1 | Lung Cancer |
| 1 | Secondary | 1 | High Blood Pressure |
| 1 | Secondary | 2 | Heart Attack |
| 1 | Secondary | 3 | Gastritis |
| 2 | Main | 1 | Diabetes |
| 2 | Secondary | 1 | Kidney Malfunction |
| 3 | Main | 1 | Flu |
+---------------+---------------+----------------+---------------------+
The DiagnosisOrder for each 'Main' DiagnosisType is 1, and for 'Secondary' DiagnosisType of the same MedicalCardId, it restarts to increase from 1.
I would like to merge multiple rows of the same MedicalCardId into a single row, and each Symptom becomes a new column depending on its DiagnosisType and DiagnosisOrder
The query result is expected to be like:
+---------------+-------------+---------------------+-------------------+-------------------+
| MedicalCardId | MainSymptom | SecondarySymptom1 | SecondarySymptom2 | SecondarySymptom3 |
+---------------+-------------+---------------------+-------------------+-------------------+
| 1 | Lung Cancer | High Blood Pressure | Heart Attack | Gastritis |
| 2 | Diabetes | Kidney Malfunction | | |
| 3 | Flu | | | |
+---------------+-------------+---------------------+-------------------+-------------------+
I've tried using PIVOT, but I'm unable to apply it to my practice.
You can try with conditional aggregation -
select MedicalCardId,
max(case when DiagnosisType='Main' then Symptom end) as MainSymptom,
max(case when DiagnosisType='Secondary' and DiagnosisOrder=1 then Symptom end) as SecondarySymptom1,
max(case when DiagnosisType='Secondary' and DiagnosisOrder=2 then Symptom end) as SecondarySymptom2,
max(case when DiagnosisType='Secondary' and DiagnosisOrder=3 then Symptom end) as SecondarySymptom3
from tablename
group by MedicalCardId
I believe you need to create a dynamic pivot table. The reason why you can’t use a normal pivot table query is because you don’t know how many Secondary Symptoms there are and therefore you don’t know how many columns to create. Below is a stored procedure that works. The first step is creating a VARCHAR (#Columns) variable that will be used to store the dynamic column names these will be [Main], [Secondary1], [Secondary2], [Secondary3] so on and so forth (I used a case statement to create the column names per your expected query result). The second step is creating another VARCHAR (#SQL) variable that will contain the pivot table SQL query. In this step you will use string concatenation to put this variable together.
Kris Wenzel has a great tutorial on dynamic pivot tables at essentialsql.com here is the link https://www.essentialsql.com/create-dynamic-pivot-table-sql-server/
Here is the stored procedure.
USE [TestDB]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author: <Author,,Name>
-- Create date: <Create Date,,>
-- Description: <Description,,>
-- =============================================
CREATE PROCEDURE [dbo].[GenerateData]
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
--GATHER PIVOT COLUMNS DYNAMICALLY
DECLARE #Columns as VARCHAR(MAX)
SELECT #Columns =
COALESCE(#Columns + ', ','') + QUOTENAME([Diagnosis])
FROM
(SELECT DISTINCT case when [DiagnosisOrder] = 1 and [DiagnosisType] = 'Main' then 'MainSymptom' else 'SecondarySymptom' + CAST([DiagnosisOrder] AS VARCHAR) end [Diagnosis] FROM [TestDB].[dbo].[test] ) AS B
ORDER BY B.[Diagnosis]
--CREATE SQL QUERY FOR PIVOT TABLE
DECLARE #SQL as VARCHAR(MAX)
SET #SQL = 'SELECT MedicalCardId, ' + #Columns + '
FROM
(
select [MedicalCardId]
,[Diagnosis]
,[Sympton]
from
(
SELECT [MedicalCardId]
,case when [DiagnosisOrder] = 1 and [DiagnosisType] = ''Main'' then ''MainSymptom'' else ''SecondarySymptom'' + CAST([DiagnosisOrder] AS VARCHAR) end [Diagnosis]
,[Sympton]
FROM [TestDB].[dbo].[test]
) A
) t
PIVOT(
MAX([Sympton])
FOR [Diagnosis] IN (' + #Columns + ')
) AS pivot_table order by [MedicalCardId]'
--EXECUTE SQL
EXEC(#SQL)
END
GO