Output multiple rows after joining and pivoting multiple tables - sql

I have a couple of tables (in a SQL Server 2014 SP2 database) on which I have successfully pivoted (thanks to lptr in Creating SQL pivot where duplicate column names exist). The next problem is that each datasource has one or more datapoints, but I am only getting one of the datapoints, per datasource.
The tables looks like this:
dataSourceSnapshot
| snapshotId | snapshotTime |
-----------------------------------
| 1 | 2021-12-01 07:00 |
| 2 | 2021-12-02 07:00 |
...
datasource
| snapshotId | dsRecordId | dsId | dsName | dsTableDate |
---------------------------------------------------------------
| 1 | 1 | 1 | cpu | 2021-12-01 07:00 |
| 1 | 2 | 2 | mem | 2021-12-01 07:00 |
| 1 | 3 | 3 | cache | 2021-12-01 07:00 |
| 2 | 4 | 1 | cpu | 2021-12-01 07:00 |
| 2 | 5 | 2 | mem | 2021-12-01 07:00 |
| 2 | 6 | 3 | cache | 2021-12-01 07:00 |
...
datasourceProperty
This is a truncated list of datasource properties.
| dsRecordId | dsPropPropertyRecordId | dsPropPropertyName | dsPropPropertyValue |
----------------------------------------------------------------------------------
| 1 | 1 | id | 1 |
| 1 | 2 | description | cpu stats |
| 1 | 3 | name | cpu |
| 1 | 4 | collectInterval | 300 |
| 1 | 5 | group | |
| 1 | 6 | dataPoints | System.Object[] |
| 2 | 7 | id | 2 |
| 2 | 8 | description | memory stats |
| 2 | 9 | name | mem |
| 2 | 10 | collectInterval | 300 |
| 2 | 11 | group | |
| 2 | 12 | dataPoints | System.Object[] |
| 3 | 13 | id | 3 |
| 3 | 14 | description | |
| 3 | 15 | name | cache |
| 3 | 16 | collectInterval | 600 |
| 3 | 17 | group | |
| 3 | 18 | dataPoints | System.Object[] |
...
datapointProperty
This is a truncated list of datapoint properties.
| dsRecordId | dpRecordId | dpPropertyName | dpPropertyValue |
-----------------------------------------------------------------
| 1 | 1 | id | 1 |
| 1 | 2 | datasourceId | 1 |
| 1 | 3 | name | cpuState |
| 1 | 4 | description | |
| 1 | 5 | alertExpr | != 0 0 |
| 1 | 6 | type | 2 |
| 1 | 7 | id | 2 |
| 1 | 8 | datasourceId | 1 |
| 1 | 9 | name | freePercent |
| 1 | 10 | description | CPU utilization |
| 1 | 11 | alertExpr | >= 90 90 |
| 1 | 12 | type | 2 |
| 2 | 13 | id | 3 |
| 2 | 14 | datasourceId | 2 |
| 2 | 15 | name | freePercent |
| 2 | 16 | description | Memory utilization |
| 2 | 17 | alertExpr | >= 90 90 |
| 2 | 18 | type | 2 |
| 3 | 19 | id | 4 |
| 3 | 20 | datasourceId | 3 |
| 3 | 21 | name | state |
| 3 | 22 | description | |
| 3 | 23 | alertExpr | = 1 1 1 |
| 3 | 24 | type | 4 |
...
I am trying to get a row per datasource (but only the most recent instance of that datasource's entry) and per datapoint, which includes all of the datasource properties. Something like this:
| dsRecordId | id | description | name | collectInterval | group | dataPoints | dp_id | dp_datasourceId | dp_name | dp_description | dp_alertExpr | dp_type |
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| 1 | 1 | cpu stats | cpu | 300 | | System.Object[] | 1 | 1 | cpuState | | != 0 0 | 2 |
| 1 | 1 | cpu stats | cpu | 300 | | System.Object[] | 2 | 1 | freePercent | | >= 90 90 | 2 |
| 2 | 2 | memory stats | mem | 300 | | System.Object[] | 3 | 2 | freePercent | Memory utilization | >= 90 90 | 2 |
| 3 | 3 | | cache | 600 | | System.Object[] | 4 | 3 | state | | = 1 1 1 | 4 |
I have a bit of T-SQL that will pivot, but because datasources and datapoints both have id, name, and description properties, I have to exclude those properties in the pivot. This query:
DECLARE
#dsPropPropertyColumns NVARCHAR(MAX) = '',
#dpPropertyColumns NVARCHAR(MAX) = '',
#sql NVARCHAR(MAX) = '';
-- select the property names
--- properties in the datasourceProperty table
SELECT #dsPropPropertyColumns = (
SELECT DISTINCT '[' + [dsPropPropertyName] + ']' + ','
FROM dbo.dataSourceSnapshot dss
LEFT OUTER JOIN dbo.datasource ds ON ds.snapshotId = dss.snapshotId
LEFT OUTER JOIN dbo.datasourceProperty dsp ON dsp.dsRecordId = ds.dsRecordId
WHERE dss.snapshotTime = (
SELECT MAX(snapshotTime) FROM dbo.dataSourceSnapshot
)
FOR XML PATH(''), TYPE
).value('.', 'nvarchar(max)')
--- properties in the datapointProperty table
SELECT #dpPropertyColumns = (
SELECT DISTINCT '[' + [dpPropertyName] + ']' + ','
FROM dbo.dataSourceSnapshot dss
LEFT OUTER JOIN dbo.datasource ds ON ds.snapshotId = dss.snapshotId
LEFT OUTER JOIN dbo.datapointProperty dp ON dp.dsRecordId = ds.dsRecordId
WHERE dss.snapshotTime = (
SELECT MAX(snapshotTime) FROM dbo.dataSourceSnapshot
)
FOR XML PATH(''), TYPE
).value('.', 'nvarchar(max)')
-- remove the trailing comma
SET #dsPropPropertyColumns = LEFT(#dsPropPropertyColumns, LEN(#dsPropPropertyColumns) - 1);
SET #dpPropertyColumns = LEFT(#dpPropertyColumns, LEN(#dpPropertyColumns) - 1);
-- construct dynamic SQL
SET #sql ='
SELECT * FROM (
SELECT dsp.dsRecordId, dsp.dsPropPropertyName, dsp.dsPropPropertyValue, CONCAT(''dp_'', dp.dpPropertyName) as dpPropertyName, dp.dpPropertyValue
FROM dbo.snapshot dss
LEFT OUTER JOIN dbo.datasource ds ON ds.snapshotId = dss.snapshotId
LEFT OUTER JOIN dbo.datasourceProperty dsp ON dsp.dsRecordId = ds.dsRecordId
LEFT OUTER JOIN dbo.datapointProperty dp ON dp.dsRecordId = ds.dsRecordId
WHERE dss.snapshotTime = (
SELECT MAX(snapshotTime) FROM dbo.dataSourceSnapshot
)
) t1
PIVOT (
MAX(dsPropPropertyValue) FOR dsPropPropertyName IN ('+ #dsPropPropertyColumns +')
) AS pivot_table
PIVOT (
MAX(dpPropertyValue) FOR dpPropertyName IN ('+ #dpPropertyColumns +')
) AS pivot_table2
ORDER BY id
'
EXECUTE sp_executesql #sql;
Produces this output:
| dsRecordId | id | description | name | collectInterval | group | dataPoints | dp_id | dp_datasourceId | dp_name | dp_description | dp_alertExpr | dp_type |
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| 1 | 1 | cpu stats | cpu | 300 | | System.Object[] | 1 | 1 | cpuState | CPU utilization | >= 90 90 | 2 |
| 2 | 2 | memory stats | mem | 300 | | System.Object[] | 3 | 2 | freePercent | Memory utilization | >= 90 90 | 2 |
| 3 | 3 | | cache | 600 | | System.Object[] | 4 | 3 | state | | = 1 1 1 | 4 |
This output shows one row for the "cpu" datasource (which has two datapoints). Confusingly, it also shows the datapoint (dp_) description and alertExpr values for the CPU's freePercent datapoint, with the "cpuState" dp_name.
In Creating SQL pivot where duplicate column names exist, it was suggested to use:
ROW_NUMBER() OVER(partition by dp.dsRecordId, dp.dpPropertyName order by dp.dpRecordId) as dpPropertyName
With this T-SQL:
SELECT dsp.dsRecordId, dsp.dsPropPropertyName, dsp.dsPropPropertyValue, ROW_NUMBER() OVER(partition by dp.dsRecordId, dp.dpPropertyName order by dp.dpRecordId) as dpPropertyName, dp.dpPropertyValue
FROM logicmonitor.dataSourceSnapshot dss
LEFT OUTER JOIN logicmonitor.datasource ds ON ds.dataSourceSnapshotId = dss.dataSourceSnapshotId
LEFT OUTER JOIN logicmonitor.datasourceProperty dsp ON dsp.dsRecordId = ds.dsRecordId
LEFT OUTER JOIN logicmonitor.datapointProperty dp ON dp.dsRecordId = ds.dsRecordId
WHERE dss.dataSourceSnapshotTime = (
SELECT MAX(dataSourceSnapshotTime) FROM logicmonitor.dataSourceSnapshot
)
I get get a table that looks like this:
| dsRecordId | dsPropPropertyName | dsPropPropertyValue | dpPropertyName | dpPropertyValue |
--------------------------------------------------------------------------------------------------
| 1 | id | 1 | 1 | |
| 1 | description | cpu stats | 2 | |
| 1 | name | cpu | 3 | |
That does not look right, so I assume I am mis-using ROW_NUMBER() and OVER().
The question is, how do I get a pivoted row per datapoint, joined to (or joined from?) the datasourceProperty and datasourceSnapshot tables

Related

SQL Merge two Tables

Let's say I have these 2 tables:
ArticleTBL
+---------+----------+-------------+------------+
|articleid| typeid | price | user |
+---------+----------+-------------+------------+
| 0 | 2 | 1 | 122 |
| 1 | 3 | 2 | 344 |
| 2 | 3 | 1 | 455 |
| 3 | 1 | 4 | 34 |
+---------+----------+-------------+------------+
TypeTBL
+---------+----------+-------------+
|typeid | type | factory |
+---------+----------+-------------+
| 0 | wooden | factry1 |
| 1 | plastic | factry2 |
| 2 | metal | factry3 |
| 3 | sth. | factry4 |
+---------+----------+-------------+
How do I request all this information only with articleid for each row?
Isn't this what you want? Read more
SELECT a.articleid,
a.price.a.USER,
t.typeid,
t.type,
t.factory
FROM form ArticleTBL a
INNER JOIN typetbl t
ON a.typeid = t.typeid
WHERE a.articleid = 0

How do I get around aggregate function error?

I have the following sql to calculate a % total:
SELECT tblTourns_atp.ID_Ti,
Sum([FS_1]/(SELECT Sum(FSOF_1)
FROM stat_atp
WHERE stat_atp.ID_T = tblTourns_atp.ID_T)) AS S1_IP
FROM stat_atp
INNER JOIN tblTourns_atp ON stat_atp.ID_T = tblTourns_atp.ID_T
GROUP BY tblTourns_atp.ID_Ti
I'm getting the 'aggregate error' because it wants the ID_T fields either grouped or in an aggregate function. I've read loads of examples but none of them seem to apply when the offending field is the subject of 'WHERE'.
Tables and output as follows:
+----------+------+--------+--+---------------+-------+--+--------+--------+
| stat_atp | | | | tblTourns_atp | | | Output | |
+----------+------+--------+--+---------------+-------+--+--------+--------+
| ID_T | FS_1 | FSOF_1 | | ID_T | ID_Ti | | ID_Ti | S1_IP |
| 1 | 20 | 40 | | 1 | 1 | | 1 | 31.03% |
| 2 | 30 | 100 | | 2 | 1 | | 2 | 28.57% |
| 3 | 40 | 150 | | 3 | 1 | | 3 | 33.33% |
| 4 | 30 | 100 | | 4 | 2 | | | |
| 5 | 30 | 100 | | 5 | 2 | | | |
| 6 | 40 | 150 | | 6 | 2 | | | |
| 7 | 20 | 40 | | 7 | 3 | | | |
| 8 | 30 | 100 | | 8 | 3 | | | |
| 9 | 40 | 150 | | 9 | 3 | | | |
| 10 | 20 | 40 | | 10 | 3 | | | |
+----------+------+--------+--+---------------+-------+--+--------+--------+
Since you already have an inner join between the two tables, a separate subquery isn't required:
select t.id_ti, sum(s.fs_1)/sum(s.fsof_1) as pct
from tbltourns_atp t inner join stat_atp s on t.id_t = s.id_t
group by t.id_ti

How to update table 2 from the inserted data in table 1?

Can you help me on what query I to to update one table with data from another.
I have 2 tables for example:
tbl_med_take
| id | name | med | qty |
---------------------------------
| 1 | jayson | med2 | 3 |
| 2 | may | med2 | 4 |
| 3 | jenny. | med3 | 6 |
| 4 | joel. | med3 | 4 |
tbl_med
| id | med | stocks |
-----------------------------
| 1 | med1 | 20 |
| 2 | med2 |. 17 |
| 3 | med3 | 24 |
The output that I want in tbl_med:
tbl_med
| id | med | stocks |
-----------------------------
| 1 | med1 | 20 |
| 2 | med2 |. 10 |
| 3 | med3 | 14 |
First get the total consumed from med_tbl_take using
select med,sum(quantity) as total from tbl_med_take group by med
Then you can left join with your med_tbl and subtract.
select m.id,m.med,(m.stocks-ISNULL(n.total,0)) from tbl_med m
left join
(select med,sum(quantity) as total from tbl_med_take group by med) n
on m.med=n.med
CHECK DEMO HERE

Avoid repeated values in a join

I have two tables - a header and a matrix/details.
*Header Table* *Matrix / Details Table*
+----+--------+-----+ +----+--------+------+
| ID | Parent | Qty | | ID | Child | Qty |
+----+--------+-----+ +----+--------+------+
| 1 | A | 10 | | 1 | X | 100 |
| 2 | B | 20 | | 1 | Y | 1000 |
| 3 | C | 30 | | 2 | X | 200 |
+----+--------+-----+ | 2 | Y | 2000 |
| 3 | X | 30 |
| 3 | Y | 300 |
| 3 | Z | 3000 |
+----+--------+------+
I'm Joining these two tables based on ID.
I don't want the result to have duplicated values from header table.
I expect a result like following:
*Current Result* *Expected Result*
+----+--------+-----+-------+------+ +----+--------+-----+-------+------+
| ID | Parent | Qty | Child | Qty | | ID | Parent | Qty | Child | Qty |
+----+--------+-----+-------+------+ +----+--------+-----+-------+------+
| 1 | A | 10 | X | 100 | | 1 | A | 10 | X | 100 |
| 1 | A | 10 | Y | 1000 | | | | | Y | 1000 |
| 2 | B | 20 | X | 200 | | 2 | B | 20 | X | 200 |
| 2 | B | 20 | Y | 2000 | | | | | Y | 2000 |
| 3 | C | 30 | X | 30 | | 3 | C | 30 | X | 30 |
| 3 | C | 30 | Y | 300 | | | | | Y | 300 |
| 3 | C | 30 | Z | 3000 | | | | | Z | 3000 |
+----+--------+-----+-------+------+ +----+--------+-----+-------+------+
Is this possible? If not any, alternate solution available?
Thanks in advance...
If you are using SQL Server,Try with the below query.
;WITH CTE_1
AS
(SELECT *,ROW_NUMBER()OVER(PARTITION BY ID,Parent,Quantity ORDER BY ID ) RNO
FROM Header H
JOIN [Matrix / Details] M
ON H.ID=M.ID)
SELECT CASE WHEN RNO=1 THEN CAST(ID as VARCHAR(50)) ELSE '' END ID,
CASE WHEN RNO=1 THEN Parent ELSE '' END Parent,
CASE WHEN RNO=1 THEN cast(Quantity as VARCHAR(50)) ELSE '' END Quantity,
Child,Qty
FROM CTE_1
ORDER BY ID,Parent,Quantity

Ask about query in sql server

i have table like this:
| ID | id_number | a | b |
| 1 | 1 | 0 | 215 |
| 2 | 2 | 28 | 8952 |
| 3 | 3 | 10 | 2000 |
| 4 | 1 | 0 | 215 |
| 5 | 1 | 0 |10000 |
| 6 | 3 | 10 | 5000 |
| 7 | 2 | 3 |90933 |
I want to sum a*b where id_number is same, what the query to get all value for every id_number? for example the result is like this :
| ID | id_number | result |
| 1 | 1 | 0 |
| 2 | 2 | 523455 |
| 3 | 3 | 70000 |
This is a simple aggregation query:
select id_number, sum(a*b)
from t
group by id_number
I'm not sure what the first column is for.