I have some data in SQL Server as:
att1 att2 att3 att4 att5 ... att205
---------------------------------------
438 498 3625 3645 5000 ... 5000
438 498 3625 3648 5000 ... 5040
438 498 3625 3629 5000 ... 5330
437 501 3625 3626 5000 ... 5040
438 498 3626 3629 5000 ... 5050
I want to know the square root of the sum for each column of data, to do so I do:
CREATE VIEW VIEW_myTable (ID, Value) AS (
SELECT 1, SQRT(SUM(att1)) FROM myTABLE
UNION ALL SELECT 2, SQRT(SUM(att2)) FROM myTABLE
UNION ALL SELECT 3, SQRT(SUM(att3)) FROM myTABLE
UNION ALL SELECT 4, SQRT(SUM(att4)) FROM myTABLE
...
UNION ALL SELECT 205, SQRT(SUM(att205)) FROM myTABLE
) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'myTABLE'
Also would like to ADD a extra result, the number of rows it sumed...
So in the example above there are 5 rows and 205 columns.
How can I take advantage of the scan sql did to the table while suming the elements?
so I do not do something like another SELECT aka.
SELECT COUNT(*) FROM [myTABLE]
In fewer words I want to take advantage of scanning the table in that query...
I was thinking to put Something like SUM(1) somewhere but do not know where....
I used the FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'myTABLE' because I am doing dynamic sql... but the query looks like that...
----------------EDIT----------------------
As answered by #Neil Moss:
if I would have lots of columns, more than 1024... SQL will not support this... Is there a way to overcome this situation?
If you are prepared to accept the query returning a single row, with a SqrtSumAttrX column for each AttrX column, then dynamically constructing the following works just fine:
select
sqrt(sum(attr1)) as SqrtSumAttr1,
sqrt(sum(attr2)) as SqrtSumAttr2,
sqrt(sum(attr3)) as SqrtSumAttr3,
...
sqrt(sum(attr205)) as SqrtSumAttr205,
sum(1) as RowsScanned
from
MyTable
This has the bonus of only scanning the table once, whereas the sample in your question scans it 205 times - once for each union.
Input:
Attr1 Attr2 Attr3
1 2 3
4 5 6
7 8 9
10 11 12
Output:
SqrtSumAttr1 SqrtSumAttr2 SqrtSumAttr3 RowsScanned
4.69041575982343 5.09901951359278 5.47722557505166 4
EDIT post-acceptance
For dynamic construction, try this:
declare #columns nvarchar(max)
declare #sql nvarchar(max)
set #columns = ''
select
#columns = #columns + 'sqrt(sum([' + [COLUMN_NAME] + '])) as SumSqrt' + [COLUMN_NAME] + ','
from
[INFORMATION_SCHEMA].[COLUMNS]
where
TABLE_NAME = 'MyTable'
and
DATA_TYPE in ('int', 'decimal', 'float') -- numerics only (add other datatypes as needed)
order by
ORDINAL_POSITION
set #sql = 'select ' + #columns + 'count(*) as RowsScanned from Results'
exec (#sql)
I've used count(*) rather than sum(1) as suggested by Andriy M as this returns 0 if no rows exist, rather than null.
Related
I am get data from a third party database to generate certain reports. The issue is that it keeps on generating a new table every month to insert the same record in same columns in the following manner:
For December table : Data_12_2019
For January table : Data_1_2020
For February table : Data_2_2020
For March table : Data_3_2020 and so on
and table data is as following:
UserId DLogId LogDate
1461 1799 2020-01-06 09:07:51.000
1462 1803 2020-01-07 09:29:39.000
1463 1806 2020-01-07 11:43:11.000
1464 1807 2020-01-09 09:16:47.000
1457 1819 2020-01-10 09:00:56.000
table2
UserId DLogId LogDate
1466 1853 2020-02-03 09:11:54.000
1468 1831 2020-02-04 09:17:09.000
1470 1833 2020-02-5 09:01:06.000
and need output in a single table in following manner:
UserId DLogId LogDate
1461 1799 2020-01-06 09:07:51.000
1462 1803 2020-01-07 09:29:39.000
1463 1806 2020-01-07 11:43:11.000
1464 1807 2020-01-09 09:16:47.000
1457 1819 2020-01-10 09:00:56.000
1466 1853 2020-02-03 09:11:54.000
1468 1831 2020-02-04 09:17:09.000
1470 1833 2020-02-5 09:01:06.000
I want to get data placed in above tables and tables that will be generated in future also.
I am seeking a help about how to do it.
You can achieve this using dynamic query. If your table structure is same, you can try like following.
DECLARE #query AS NVARCHAR(max) = Stuff((SELECT DISTINCT ' ' + 'SELECT * FROM ' + Quotename(name) + ' UNION'
FROM
(
SELECT
[name]
FROM
sys.sysobjects
WHERE
[xtype] = 'U'
and NAME LIKE 'Data_[0-9][0-9]_[0-9][0-9][0-9][0-9]'
)t
FOR xml path(''), type).value('.', 'NVARCHAR(MAX)'), 1, 1, '');
SET #query= substring(#query,0,len(#query)-5)
exec(#query)
Note: Better replace * with the column names you want.
To get all the tables you can use sysobject and filter out based on table names like
SELECT [name]
FROM sys.sysobjects
WHERE [xtype] = 'U'
AND NAME LIKE 'Data_[0-9][0-9]_[0-9][0-9][0-9][0-9]'
And build a dynamic query using UNION.
PREPARE stmt1 FROM ' SELECT GROUP_CONCAT('SELECT * FROM ',TABLE_NAME,' UNION ')
FROM information_schema.tables
WHERE table_schema =? AND table_name LIKE 'Data_%'';
SET #a = 'MyDb';
EXECUTE stmt1 USING #a;
I understand how to use PIVOT to rotate rows into columns but I have a unique scenario where rows also have to be grouped. The source data is from a NoSQL database schema (un-relational). Here is an example of the source data:
ID case_id field_id sequence_number textvalue
1 12897 25 100 AAAAA
2 12897 50 100 BBBBB
3 12897 75 100 CCCCC
4 13587 25 200 DDDDD
5 13587 50 200 EEEEE
6 13587 75 200 FFFFF
7 13587 100 200 GGGGG
The result I need is:
case_id value_field_id_25 value_field_id_50 value_field_id_75 value_field_id_100
12897 AAAAA BBBBB CCCCC
13587 DDDDD EEEEE FFFFF GGGGG
So, what I need is a row of related records grouped by sequence_number. The number of rows with the same sequence_number is dynamic (it varies).
Any ideas?
Declare #SQL varchar(max) = Stuff((Select ',' + QuoteName('value_field_id_'+cast(field_id as varchar(25)))
From (Select Distinct Top 100 Percent field_id
From YourTable
Order by 1) A
For XML Path('')),1,1,'')
Select #SQL = '
Select [case_id],' + #SQL + '
From (
Select sequence_number
,case_id
,ColName = ''value_field_id_''+cast(field_id as varchar(25))
,Value = textvalue
From YourTable A
) A
Pivot (max([Value]) For [ColName] in (' + #SQL + ') ) p'
Exec(#SQL);
Returns
I need to convert the table from (A) into (B).
I am able to get it work by joining the same table multiple times and use Max operator to assign the fields, but is there any better way to achieve this as Max operator could cause performance issue on huge table.
Can this be done by using pivot and will it cause any performance issue on huge table?
Btw, ID in below example is only 1 of the fields as example, there are other fields that need to achieve the same thing.
(A)
Class ID
1 11
1 12
1 13
2 11
2 12
2 13
(B)
Class ID2 ID3 ID4
1 11 12 13
2 11
12 13
You can you PIVOT:
SELECT *
FROM
(SELECT * FROM MY_TABLE
) pivot ( MAX(id) FOR id IN ([11],[12],[13]) );
Assuming you need to go DYNAMIC
Declare #SQL varchar(max) = Stuff((Select Distinct ',' + QuoteName(concat('ID',1+Row_Number() over (Partition By Class Order By ID))) From YourTable Order by 1 For XML Path('')),1,1,'')
Select #SQL = '
Select [Class],' + #SQL + '
From (
Select [Class]
,ID
,Col = concat(''ID'',1+Row_Number() over (Partition By [Class] Order By [ID]))
From YourTable
) A
Pivot (max(ID) For [Col] in (' + #SQL + ') ) P'
Exec(#SQL);
Returns
Class ID2 ID3 ID4
1 11 12 13
2 11 12 13
The system we are using allows a data entry form to be created from multiple user defined fields to satisfy information required on a particular group of different "ORDES". The fields are then stored in a database as such from what is entered:
GUID OrderGUID UserDataCode Value
1 100 OrderName Breakfast
2 100 OrderDesc Food you eat before Lunch
3 100 CerealYN Y
4 100 ToastYN Y
5 100 ToastDesc White Bread
6 100 PaperYN Y
7 100 PaperDesc The Newsroom
8 101 OrderName Lunch
9 101 OrderDesc Food you eat before Dinner
10 101 CerealYN N
11 101 ToastYN Y
12 101 ToastDesc Brown Bread
13 101 PaperYN Y
14 101 PaperDesc The MiddayNews
(etc)
(in fact this is an Enterprise Hospital software but I have used simpler examples here)
I would like using SQL to return this table PIVOTed like below
OrderGUID OrderName OrderDESC CerealYN ToastYN ToastDesc ....
101 Breakfast Food you.. Y Y White Bread ....
102 Lunch Food you.. N Y Brown Bread ....
I wrote the following SQL based on examples found on the net:
DECLARE #DynamicPivotQuery AS NVARCHAR(MAX)
DECLARE #ColumnName AS NVARCHAR(MAX)
--Get distinct values of the PIVOT Column
SELECT #ColumnName= ISNULL(#ColumnName + ',','')
+ QUOTENAME([UserDataCode])
FROM (
SELECT
[UserDataCode]
FROM
[XXX].[dbo].[CV3OrderUserData]
WHERE OrderGUID = 3000680
) AS Codes;
--Prepare the PIVOT query using the dynamic
SET #DynamicPivotQuery = N'SELECT OrderGUID, ' + #ColumnName + '
FROM
[XXX].[dbo].[CV3OrderUserData]
PIVOT(Max(Value)
FOR UserDataCode IN (' + #ColumnName + ')) AS PVTTable'
--Execute the Dynamic Pivot Query
--SELECT #DynamicPivotQuery
EXEC sp_executesql #DynamicPivotQuery
However while it does the pivot as requested.. and puts the values in the correct new "dynamic" columns, if returns a row for each OrderGUID + Value,
ie:
OrderGUID OrderName OrderDesc CerealYN ToastYN
100 Breakfast null null null ...
100 null Food you.. null null ...
101 null null Y null ...
etc.etc
What am i doing wrong :( ?
The problem in your query is the pivot source query has GUID column which makes the pivot operator to consider GUID column.
To get the expected output you need to remove GUID column from the pivot source query.
Here is a static version you can convert it to dynamic version as you already did.
select * from
(
SELECT OrderGUID,UserDataCode,Value
FROM
tst) A
PIVOT(Max(Value)
FOR UserDataCode IN ([OrderName],[OrderDesc],
[CerealYN],[ToastYN],
[ToastDesc],[PaperYN],
[PaperDesc])) AS PVTTable
SQLFIDDLE DEMO
I have below mentioned table :
drn RecNum Name Value
----------------------------------------------
1 1 ad1_pk 1
2 1 ad1_address1 P.O. Box 5036
3 1 ad1_address2 NULL
4 1 ad1_address3 NULL
5 1 ad1_ctyfk 56
6 1 ad1_postalcode 80155-5036
7 1 ad1_active Y
8 1 ad1_irstat A
9 1 ad1_irdata NULL
10 1 ad1_at1fk 1
1 2 ad1_pk 2
2 2 ad1_address1 1871 S. Broadway
3 2 ad1_address2 NULL
4 2 ad1_address3 NULL
5 2 ad1_ctyfk 1
6 2 ad1_postalcode 80210
7 2 ad1_active Y
8 2 ad1_irstat A
9 2 ad1_irdata NULL
10 2 ad1_at1fk 1
I am creating the pivot using the below mentioned query:
declare #var nvarchar(max)
declare #sql nvarchar(max)
set #var = stuff((select distinct ',' + name from temp
for xml path('')),1,1,'') -- **this is giving distinct column list but the order of columns get changed..**
set #sql = 'select * from temp
pivot(max(value) for name in (' + #var + ')) as pvt'
exec sp_executesql #sql
Is there a way to keep the order of the columns unchanged? I want the order of columns listed in #var to be same as in the table.
Add a GROUP BY and an ORDER BY clause (to replace the DISTINCT) where you build your column list as follows:
set #var = stuff((select ',' + min(name) from temp GROUP BY drn ORDER BY drn
for xml path('')),1,1,'')
And don't forget the the necessary aggregation (I've used MIN()). Thanks #Ionic.
This is because you're using a DISTINCT in your SELECT query. If you look at the execution plan, you can see DISTINCT SORT operation. This sorts your result based on the DISTINCT columns you specify, in this case it's Name:
To retain the order, you can try this:
set #var = stuff((
select ',' + name
from(
select
name,
drn,
rn = row_number() over(partition by name order by drn)
from temp
)t
where rn = 1
order by drn
for xml path('')),
1,1,'')