I have a DataSet like below :
Where B1,B2 C1,C2 and C3 are the Column names. G1,G2,S1 and T1 are the first Row elements of my Data Set.
Now i want to combine Similar Columns/ROws in to Group .
Example : Columns B1, B2 and B3 combines to a single group B,
Rows : G1 and G2 Combines together to form a single row G.
Below is the O/P DataSet i need.
I have tried to use Dictionaries and DataSet Loops using but cant get this O/P .
Can anyone help me out with this.
This having to be dynamic adds a huge amount of complexity to this solution. As you haven't responded to the version question, I have not used STRING_AGG, however, if you are using SQL Server 2017+ you can simplify the query to use it.
Firstly, some sample data:
CREATE TABLE dbo.Matrix ([Data] char(2),
B1 tinyint,
B2 tinyint,
C1 tinyint,
C2 tinyint,
C3 tinyint)
INSERT INTO dbo.Matrix ([Data],
B1,
B2,
C1,
C2,
C3)
VALUES('G1',1,1,2,2,4),
('G2',1,1,1,1,1),
('S1',2,1,2,1,1),
('T1',1,3,2,2,3);
GO
Now, if this wasn't dynamic, you could use a a Cross tab to pivot the data into groups, like this:
SELECT LEFT(M.[Data],1) AS [Data],
SUM(CASE V.Col WHEN 'B' THEN V.ColVal END) AS B,
SUM(CASE V.Col WHEN 'C' THEN V.ColVal END) AS C
FROM dbo.Matrix M
CROSS APPLY(VALUES('B',M.B1),
('B',M.B2),
('C',M.C1),
('C',M.C2),
('C',M.C3))V(Col,ColVal)
GROUP BY LEFT(M.[Data],1);
Unfortunately, as it is dynamic then we need dynamic SQL. Honestly, this isn't beginning stuff, and I'm not here to support this SQL; it's up to you to understand it, maintain it, support it, and (because it is dynamic SQL) keep it secure. I'm happy to answer some questions on how it works, but for someone that doesn't knowe SQL well this is a steep learning curve:
DECLARE #SQL nvarchar(MAX);
SET #SQL = N'SELECT LEFT(M.[Data],1) AS [Data],' + NCHAR(13) + NCHAR(10) +
STUFF((SELECT N',' + NCHAR(13) + NCHAR(10) +
N' SUM(CASE V.Col WHEN N' + QUOTENAME(LEFT(C.COLUMN_NAME,1),'''') + N' THEN V.ColVal END) AS ' + QUOTENAME(LEFT(C.COLUMN_NAME,1))
FROM INFORMATION_SCHEMA.COLUMNS C
WHERE C.TABLE_SCHEMA = N'dbo'
AND C.TABLE_NAME = N'Matrix'
AND C.COLUMN_NAME != N'Data' --Assumes that all other columns are applicable
GROUP BY LEFT(C.COLUMN_NAME,1)
ORDER BY LEFT(C.COLUMN_NAME,1)
FOR XML PATH(N''),TYPE).value('.','nvarchar(MAX)'),1,3,N'') + NCHAR(13) + NCHAR(10) +
N'FROM dbo.Matrix M' + NCHAR(13) + NCHAR(10) +
N' CROSS APPLY(VALUES' + STUFF((SELECT ',' + NCHAR(13) + NCHAR(10) +
N' (N' + QUOTENAME(LEFT(C.COLUMN_NAME,1),'''') + N',M.' + QUOTENAME(C.COLUMN_NAME) + N')'
FROM INFORMATION_SCHEMA.COLUMNS C
WHERE C.TABLE_SCHEMA = N'dbo'
AND C.TABLE_NAME = N'Matrix'
AND C.COLUMN_NAME != N'Data' --Assumes that all other columns are applicable
ORDER BY C.COLUMN_NAME
FOR XML PATH(N''),TYPE).value('.','nvarchar(MAX)'),1,26,N'') + N')V(Col,ColVal)' + NCHAR(13) + NCHAR(10) +
N'GROUP BY LEFT(M.[Data],1)' + NCHAR(13) + NCHAR(10) +
N'ORDER BY LEFT(M.[Data],1);';
PRINT #SQL; --Your debugging best friend.
EXEC sp_executesql #SQL;
db<>fiddle
You can apply below logic to get desired output from SQL Server:
create table T_DataSet
(
Data varchar(50),
B1 int,
B2 int,
C1 int,
C2 int,
C3 int
)
INSERT INTO T_DataSet ([Data],B1,B2,C1,C2,C3)
VALUES('G1',1,1,2,2,4),
('G2',1,1,1,1,1),
('S1',2,1,2,1,1),
('T1',1,3,2,2,3);
DECLARE #QUERY VARCHAR(MAX)=''
DECLARE #Columns VARCHAR(MAX)=''
;with tbl_COLUMN_NAME (COLUMN_NAME) AS
(
select name as COLUMN_NAME from sys.all_columns
where object_id = (select object_id from sys.tables where name = 'T_DataSet')
and name <> 'Data'
)
SELECT
#Columns = ISNULL(#Columns +',', '') + T.COLUMN_NAME
FROM
(
select
COLUMN_NAME = 'SUM(' +
(select SUBSTRING(
(
SELECT '+'+ COLUMN_NAME
FROM tbl_COLUMN_NAME
where LEFT(COLUMN_NAME,1) = LEFT(inner_C1.COLUMN_NAME,1)
FOR XML PATH('')
), 2 , 9999))
+ ') AS ' + LEFT(COLUMN_NAME,1)
from tbl_COLUMN_NAME as inner_C1
Group by LEFT(COLUMN_NAME,1)
)T
set #QUERY = 'select LEFT([Data],1) as Data ' + #Columns + '
From T_DataSet
Group by LEFT([Data],1)';
PRINT #QUERY
EXEC(#QUERY)
Related
I want to duplicate all my tables in SQL Server, all table names would have had "temp" added at the beginning. And all of them would have had added an extra column (the same to all). I don't need whole code, just general idea how to do it.
A straightforward way to go:
You need to fetch the table names from your database (probably using INFORMATION_SCHEMA.TABLES).
For each of those tables from step 1, you need to generate a corresponding SELECT ... INTO statement.
You need to execute each generated SQL statement from step 2.
You already have a solution with a cursor. This is one without a cursor:
DECLARE #script VARCHAR(MAX) = '';
SELECT #script = #script + 'SELECT * INTO [temp'+ TABLE_NAME +'] FROM [' + TABLE_NAME + '];' + CHAR(13) + CHAR(10) FROM INFORMATION_SCHEMA.TABLES
EXEC (#script);
Remark: The CHAR(13) + CHAR(10) is not necessary; just added for readability if you want to check the script first (using PRINT instead EXEC).
Edit:
An additional question in the comments to add a checksum value in the resulting tables could be done as follows:
DECLARE #script VARCHAR(MAX) = '';
SELECT #script = #script + 'SELECT CHECKSUM(*) AS [__checksum], * INTO [temp'+ TABLE_NAME +'] FROM [' + TABLE_NAME + '];' + CHAR(13) + CHAR(10) FROM INFORMATION_SCHEMA.TABLES
EXEC (#script);
Using HASHBYTES instead of CHECKSUM is probably better, but it accepts only two parameters: the hash algorithm and a single value to hash. So in that case, you probably need to pass a string value by manually concatenating all the fields of your tables, and that may be somewhat troublesome to add in a dynamic query like mine. It would probably result in something more complex than just three lines...
Well, something like this, actually:
DECLARE #script NVARCHAR(MAX) = N'';
WITH
[Columns] AS
(
SELECT
TABLE_NAME AS [TableName],
COLUMN_NAME AS [ColumnName],
ROW_NUMBER() OVER (PARTITION BY TABLE_NAME ORDER BY ORDINAL_POSITION) AS [ColSeq]
FROM
INFORMATION_SCHEMA.COLUMNS
),
[Tables] AS
(
SELECT
[TableName],
CAST(N'[' + [ColumnName] + N']' AS NVARCHAR(MAX)) AS [ColumnList],
[ColSeq]
FROM
[Columns] AS C
WHERE
[ColSeq] = (SELECT MAX([ColSeq])
FROM [Columns]
WHERE [TableName] = C.[TableName])
UNION ALL
SELECT T.[TableName], N'[' + C.[ColumnName] + N'], ' + T.[ColumnList], C.[ColSeq]
FROM
[Tables] AS T
INNER JOIN [Columns] AS C ON C.[TableName] = T.[TableName] AND C.[ColSeq] = T.[ColSeq] - 1
)
SELECT #script = #script + N'SELECT HASHBYTES(''md5'', CONCAT(N'''', ' + [ColumnList] + N')) AS [__checksum], * INTO [temp' + [TableName] + N'] FROM [' + [TableName] + N'];' + NCHAR(13) + NCHAR(10)
FROM [Tables]
WHERE [ColSeq] = 1;
EXEC (#script);
Remarks:
In the recursive CTE [Tables], which is used for concatenating the column names of each table in a comma-separated string value, I started at the last column and moved backwards to ease the filter condition in my main query.
I added an additional first parameter N'' to the CONCAT calls in the resulting #script contents, since the CONCAT function requires at least 2 arguments, which would be troublesome in this case when processing tables with just one column.
In this case, despite the somewhat worse performance, it might be clearer and easier to fall back to using a cursor, like #HasanMahmood suggested in his answer...
try this code:
get all the table name form information schema and run a dynamic sql to create tables
DECLARE #script varchar(max)
DECLARE db_cursor CURSOR FOR
SELECT script = 'Select * Into [temp'+ TABLE_NAME +'] From ' + QUOTENAME(TABLE_NAME) FROM INFORMATION_SCHEMA.TABLES
OPEN db_cursor
FETCH NEXT FROM db_cursor INTO #script
WHILE ##FETCH_STATUS = 0
BEGIN
EXEC(#script)
--PRINT #script
FETCH NEXT FROM db_cursor INTO #script
END
CLOSE db_cursor
DEALLOCATE db_cursor
I have 14 tables of different kinds of employees. I have a C# application which is wired to my SQL Server database and when I type a last name in a textbox it brings back a record with that last name and displays it in a listbox.
However I have managed to do this with only one table. So if I type “Jones” it will bring back and display Jones from one table.
I would like to bring back all the Jones’ from all 14 tables. In other words, when I type a last name, I need the application to show me all records of that last name from all 14 tables.
What would be a reasonable approach to this? It would be a lot easier if I had one table with all employees but I need the seperation. Basically when I click the search button I need the application to go fetch from any of the 14 tables with the given name.
What would be a suitable approach to this?
Define the following stored procedure in your database:
CREATE PROCEDURE GetAll_SP
(
#FirstName VARCHAR(50)
)
AS
BEGIN
(SELECT 1, first_name, last_name FROM UsersTable1 WHERE first_name = #FirstName)
UNION
(SELECT 2, first_name, last_name FROM UsersTable2 WHERE first_name = #FirstName)
UNION
(SELECT 3, first_name, last_name FROM UsersTable3 WHERE first_name = #FirstName)
-- ....
END
GO
or the following one instead of you don't need to have any kind of control over your users location:
CREATE PROCEDURE GetAll_SP
(
#FirstName VARCHAR(50)
)
AS
BEGIN
(SELECT first_name, last_name FROM UsersTable1 WHERE first_name = #FirstName)
UNION ALL
(SELECT first_name, last_name FROM UsersTable2 WHERE first_name = #FirstName)
UNION ALL
(SELECT first_name, last_name FROM UsersTable3 WHERE first_name = #FirstName)
-- ....
END
GO
Then, in your code:
String firstName = "Jones";
using (SqlCommand cmd = new SqlCommand("GetAll_SP", m_Connection))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#FirstName", SqlDbType.VarChar).Value = firstName;
m_Connection.Open();
cmd.ExecuteNonQuery();
}
You should repeat your Code 14 Times to collect values from all this tables or you submit a SQL Query for all 14 Tables at once with unions or 14 queries in one Statement - depending on your Data Access Technology...
Okay, to get results from all 14 tables you can use UNION ALL Operator, so your SQL would look something like:
(SELECT first_name, last_name FROM table1 WHERE first_name='John')
UNION ALL
(SELECT first_name, last_name FROM table2 WHERE first_name='John')
...
(SELECT first_name, last_name FROM table14 WHERE first_name='John')
You need to have equal amount of fields in every select. However better approach would be if you save all names (and any other shared data) in one table, and connect with Key with all those 14 tables that have different dataset. That way you can prevent such a long queries like this one above (and probably slow queries) and query would look more like this:
SELECT first_name, last_name, user_type, user_id WHERE first_name='John'
And then you can retreive fields from corresponding table as field user_type gives you info in which out of 14 tables to search for other data and user_id gives you data of that user, so second query would look something like this:
SELECT job_position, worksheet, other_data FROM tableN WHERE user_id=...
Simply set #SearchStr and every column in every table will be searched.
drop table #results
go
declare #SearchStr nvarchar(100)
set #SearchStr = 'Donna%' -- use wildcards
CREATE TABLE #Results (ColumnName nvarchar(370), ColumnValue nvarchar(3630))
SET NOCOUNT ON
DECLARE #TableName nvarchar(256), #ColumnName nvarchar(128), #SearchStr2 nvarchar(110)
SET #TableName = ''
SET #SearchStr2 = QUOTENAME('%' + #SearchStr + '%','''')
WHILE #TableName IS NOT NULL
BEGIN
SET #ColumnName = ''
SET #TableName =
(
SELECT MIN(QUOTENAME(TABLE_SCHEMA) + '.' + QUOTENAME(TABLE_NAME))
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_TYPE = 'BASE TABLE'
AND QUOTENAME(TABLE_SCHEMA) + '.' + QUOTENAME(TABLE_NAME) > #TableName
AND OBJECTPROPERTY(
OBJECT_ID(
QUOTENAME(TABLE_SCHEMA) + '.' + QUOTENAME(TABLE_NAME)
), 'IsMSShipped'
) = 0
)
WHILE (#TableName IS NOT NULL) AND (#ColumnName IS NOT NULL)
BEGIN
SET #ColumnName =
(
SELECT MIN(QUOTENAME(COLUMN_NAME))
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = PARSENAME(#TableName, 2)
AND TABLE_NAME = PARSENAME(#TableName, 1)
AND DATA_TYPE IN ('char', 'varchar', 'nchar', 'nvarchar', 'text')
AND QUOTENAME(COLUMN_NAME) > #ColumnName
)
print cast(#TableName as nvarchar(200)) + ' ' + #ColumnName
IF #ColumnName IS NOT NULL
BEGIN
INSERT INTO #Results
EXEC
(
--'SELECT ''' + #TableName + '.' + #ColumnName + ''', LEFT(' + #ColumnName + ', 3630)
--FROM ' + #TableName + ' (NOLOCK) ' +
--' WHERE ' + #ColumnName + ' LIKE ' + #SearchStr2
'SELECT ''' + #TableName + '.' + #ColumnName + ''', ' + #ColumnName + '
FROM ' + #TableName + ' (NOLOCK) ' +
' WHERE ' + #ColumnName + ' LIKE ' + #SearchStr2
)
END
END
END
SELECT ColumnName, ColumnValue FROM #Results
I've got a stored procedure which joins a number of tables to produce a large resultset which is then returned to my application. The application in turn loops through the results and combines rows on a particular ID and chooses data per row to include in a new object. This is perhaps easiest to explain using an example:
Inspection, Desc, Value
1, Description1, 3
1, Description2, 2
1, Description3, 5
This is in code turned into
Inspection, Description1, Description2, Description3
1, 3, 2, 5
The point of this is to have one row per inspection item with item description as headers and value as the cell value for inspection row and header. This is then exported to Excel.
The question is: how do I do this in SQL Server, as in expanding my SP to return a lot fewer but "wider" rows with a lot more columns?
Another complication is that one inspection may have rows which another one lacks, in that case the solution is to add an empty value or a '-'.
P.S. This is using Sql Server 2012.
If you are using mssql 2005+. You can use a pivot like this:
Test data
DECLARE #tbl TABLE(Inspection INT, [Desc] VARCHAR(100),Value INT)
INSERT INTO #tbl
VALUES
(1,'Description1', 3),
(1,'Description2', 2),
(1,'Description3', 5)
Query
SELECT
*
FROM
(
SELECT
tbl.Inspection,
tbl.[Desc],
tbl.Value
FROM
#tbl AS tbl
) AS tbl
PIVOT
(
SUM(Value)
FOR [Desc] IN ([Description1],[Description2],[Description3])
)AS pvt
Result:
Inspection, Description1, Description2, Description3
1 3 2 5
Edit
As juharr said in the comment:
The resulting column names (values in the table) are when building the query. Which might require another initial query to get
Edit 2
If you are not using mssql 2005+. Or want to have and alternitive explanation. Please see the following query:
SELECT
tbl.Inspection,
SUM(CASE WHEN [Desc]='Description1' THEN tbl.Value ELSE 0 END) AS Description1,
SUM(CASE WHEN [Desc]='Description2' THEN tbl.Value ELSE 0 END) AS Description2,
SUM(CASE WHEN [Desc]='Description3' THEN tbl.Value ELSE 0 END) AS Description3
FROM
#tbl AS tbl
GROUP BY
tbl.Inspection
This do not requiere a pivot and can be use on most of RDMS out there
You should use Sql Server Pivot. It converts rows into columns. You can have an easiest start by this example.
If you'd like to do this dynamically, without having to know what all of the Desc values are, you can build your pivot query and use Exec() or Execute sp_executesql
DECLARE #Columns NVARCHAR(MAX),
#Sql NVARCHAR(MAX)
--Build your column headers based on Distinct Desc values
SELECT #Columns = COALESCE(#Columns + ',', '') + QUOTENAME([Desc])
FROM (SELECT DISTINCT [Desc] FROM tbl) t
ORDER BY [Desc]
--Build your pivot query
SET #Sql = '
SELECT
*
FROM
tbl
PIVOT
(
MAX([Value])
FOR [Desc] IN (' + #Columns + ')
) p
'
EXEC(#Sql)
If you want - for null values, you'll need to create another variable to hold the conversion scripts for the Select part of your sql.
DECLARE #Columns NVARCHAR(MAX),
#Sql NVARCHAR(MAX),
#ColumnAliases NVARCHAR(MAX)
--Build your pivot columns based on Distinct Desc values
SELECT #Columns = COALESCE(#Columns + ',', '') + QUOTENAME([Desc])
FROM (SELECT DISTINCT [Desc] FROM tbl) t
ORDER BY [Desc]
--Build your column headers, replacing NULL with -
SELECT #ColumnAliases = COALESCE(#ColumnAliases + ',', '')
+ 'COALESCE(CONVERT(VARCHAR,' + QUOTENAME([Desc]) + '),''-'') AS ' + QUOTENAME([Desc])
FROM (SELECT DISTINCT [Desc] FROM tbl) t
ORDER BY [Desc]
--Build your pivot query
SET #Sql = '
SELECT
Inspection,'
+ #ColumnAliases + '
FROM
tbl
PIVOT
(
MAX([Value])
FOR [Desc] IN (' + #Columns + ')
) p
'
EXEC(#Sql)
I'm working on a webapp that displays data from a Microsoft SQL Server dynamic pivot table.
Normally I'd try and figure out a way to do the dynamic pivot in c#, but in this case the pivot has to be a SQL Server stored procedure because other apps also need access to the pivot table.
Here's the SQL:
DECLARE #DynamicPivot AS NVARCHAR(MAX)
DECLARE #ColumnName AS NVARCHAR(MAX)
SELECT
#ColumnName = ISNULL(#ColumnName + ',', '')
+ QUOTENAME(xml_tag_name)
FROM
(SELECT DISTINCT xml_tag_name FROM DataEntries) AS TagValues
SET #DynamicPivot =
N'SELECT DISTINCT capture_id, ' + #ColumnName + '
FROM DataEntries
PIVOT(MAX(xml_tag_value)
FOR xml_tag_name IN (' + #ColumnName + ')) AS PVTTable'
EXEC sp_executesql #DynamicPivot
All the articles I've gone through deal with normal export to Excel or static pivots. eg: Export Table from SQL Server to Excel 2007 using C#.
How do I go about exporting this dynamic pivot to Excel?
heres a example for you.
It creates dynamic pivot to global temp table and then exports it to excel.
If you find problems with export part - let me know.
CREATE TABLE Table1 (ColId INT,ColName VARCHAR(10))
INSERT INTO Table1 VALUES(1, 'Country')
INSERT INTO Table1 VALUES(2, 'Month')
INSERT INTO Table1 VALUES(3, 'Day')
CREATE TABLE Table2 (tID INT,ColID INT,Txt VARCHAR(10))
INSERT INTO Table2 VALUES (1,1, 'US')
INSERT INTO Table2 VALUES (1,2, 'July')
INSERT INTO Table2 VALUES (1,3, '4')
INSERT INTO Table2 VALUES (2,1, 'US')
INSERT INTO Table2 VALUES (2,2, 'Sep')
INSERT INTO Table2 VALUES (2,3, '11')
INSERT INTO Table2 VALUES (3,1, 'US')
INSERT INTO Table2 VALUES (3,2, 'Dec')
INSERT INTO Table2 VALUES (3,3, '25')
DECLARE #cols NVARCHAR(2000);
SELECT #cols = COALESCE(#cols + ',[' + colName + ']', '[' + colName + ']')
FROM Table1
ORDER BY colName;
IF OBJECT_ID('tempdb..##t1') IS NOT NULL
BEGIN
DROP TABLE ##t1;
END;
DECLARE #query NVARCHAR(4000);
SET #query = N'SELECT tID, ' + #cols + ' into ##t1
FROM
(SELECT t2.tID
, t1.ColName
, t2.Txt
FROM Table1 AS t1
JOIN Table2 AS t2 ON t1.ColId = t2.ColID) p
PIVOT
(
MAX([Txt])
FOR ColName IN
( ' + #cols + ' )
) AS pvt
ORDER BY tID;';
EXECUTE(#query);
SELECT *
FROM ##t1;
DECLARE #sql VARCHAR(MAX);
DECLARE #FileName VARCHAR(MAX) = 'C:\Test.xls';
SET #sql = 'INSERT INTO OPENROWSET(''Microsoft.ACE.OLEDB.12.0'',''Excel 12.0;Database='
+ #FileName + ''',''SELECT * FROM [Sheet1$]'') SELECT * FROM ##T1';
EXECUTE(#sql);
I have this View from multi tables
account_name.......bonus..........value
customer1............A............14000
customer1............B............500
customer1............C............14500
customer2............A............20000
customer2............B............200
customer2............C............20200
http://im33.gulfup.com/Nt0mM.png
how can i retrieval this view and show it on GridView this View using LINQ
...................A.........B..........C
customer1.......14000.......500.......14500
customer2.......20000.......200.......20200
SELECT account_name,
SUM(CASE WHEN bonus = 'A' THEN value ELSE 0 END) AS A,
SUM(CASE WHEN bonus = 'B' THEN value ELSE 0 END) AS B,
SUM(CASE WHEN bonus = 'C' THEN value ELSE 0 END) AS C
FROM YouView
GROUP BY account_name
I solved my problem using Pivots with dynamic as realnumber3012 recommend...
the demonstrate of the table and the result on this link
DECLARE #cols NVARCHAR(2000)
SELECT #cols = COALESCE(#cols + ',[' + bounse_name+ ']','[' + bounse_name + ']')
FROM dbo.tbl_bounse
DECLARE #query NVARCHAR(4000)
SET #query = N'SELECT account_name , '+ #cols +'
FROM
(SELECT tbl_account.account_name, tbl_account.account_career,
tbl_bounse.bounse_name, tbl_detail.detail_value
FROM tbl_account INNER JOIN
tbl_detail ON tbl_account.account_id = tbl_detail.detail_accound_id
INNER JOIN
tbl_bounse ON tbl_detail.detail_bounce_id = tbl_bounse.bounse_id
) p
PIVOT
(
MAX([detail_value])
FOR bounse_name IN
( '+
#cols +' )
) AS pvt;'
EXECUTE(#query)