Pivot both numeric and strung non-numeric columns - sql

I have a specific requirement to pivot up two columns, one numeric and one string. I am well versed with SQL pivot, however, could not reach a solution for this.
My raw data is like:
Country | Segment | Year | Parameter_Name | Parameter_Value_Numeric | Parameter_Value_String
USA | 1 | 2003 | Datapoint1 | 100 | null
USA | 1 | 2003 | Datapoint2 | 148 | null
USA | 1 | 2003 | Datapoint3 | null | Upper values(s)
USA | 2 | 2003 | Datapoint1 | 121 | null
USA | 2 | 2003 | Datapoint2 | 180 | null
USA | 2 | 2003 | Datapoint3 | null | Medium values(s)
The results I want is something like:
Country | Segment | Year | Datapoint1 | Datapoint2 | Datapoint3
USA | 1 | 2003 | 100 | 148 | Upper values(s)
USA | 2 | 2003 | 121 | 180 | Medium values(s)
The issue being faced is that Datapoint1 & Datapoint2 are float and Datapoint3 is nvarchar. Hence, pivot is able to use only one of these.

You can try this below logic-
DEMO HERE
SELECT Country,Segment,Year,
MAX(CASE WHEN Parameter_Name = 'Datapoint1' THEN Parameter_Value_Numeric END) Datapoint1,
MAX(CASE WHEN Parameter_Name = 'Datapoint2' THEN Parameter_Value_Numeric END) Datapoint2,
MAX(CASE WHEN Parameter_Name = 'Datapoint3' THEN Parameter_Value_String END) Datapoint3
FROM your_table
GROUP BY Country,Segment,Year

Related

Finding the 2 closest corresponding rows to the average of the sum of multiplied values, grouped by a matching title when given an input title

I want to find the average values of a sum of multiplied values grouped by a matching title, in order to give them a corresponding rating, and then find the 2 closest to the input value.
SELECT titleValueAVG / 3 AS average,
title
FROM (
SELECT Sum(a) AS titleValueAVG,
title
FROM (
SELECT value * 1 AS a,
title
FROM Table1
WHERE type = 'A' AND
contesting = 'yes'
UNION ALL
SELECT value * 2,
title
FROM Table1
WHERE type = 'A' AND
contesting = 'no'
UNION ALL
SELECT value * 3,
title
FROM Table1
WHERE type = 'A' AND
contesting = 'undecided'
)
GROUP BY title
ORDER BY title = 'Australia' DESC,
ABS(titleValueAVG) - (
SELECT value * 1 AS a,
title
FROM Table1
WHERE type = 'A' AND
contesting = 'yes' AND title = 'Australia'
UNION ALL
SELECT value * 2,
title
FROM Table1
WHERE type = 'A' AND
contesting = 'no' AND title = 'Australia'
UNION ALL
SELECT value * 3,
title
FROM Table1
WHERE type = 'A' AND
contesting = 'undecided' AND title = 'Australia'
)
) limit 2;
From an example table:
| Title | Type | Competing | Value |
| -------- | -------------| -------------- | -------------- |
| Australia| A | yes | 26 |
| Australia| A | no | 162 |
| Australia| A | undecided | 37 |
| Spain | A | yes | 14 |
| Spain | A | no | 101 |
| Spain | A | undecided | 11 |
| Ireland | A | yes | 124 |
| Ireland | A | no | 62 |
| Ireland | A | undecided | 9 |
| Nigeria | C | yes | 4 |
| Nigeria | C | no | 11 |
| Nigeria | C | undecided | 7 |
| Colombia | A | yes | 26 |
| Colombia | A | no | 12 |
| Colombia | A | undecided | 19 |
| Turkey | A | yes | 29 |
| Turkey | A | no | 145 |
| Turkey | A | undecided | 24 |
| Malta | B | yes | 1 |
| Malta | B | no | 11 |
| Malta | B | undecided | 4 |
| Mexico | A | yes | 74 |
| Mexico | A | no | 19 |
| Mexico | A | undecided | 12 |
| Slovenia | B | yes | 16 |
| Slovenia | B | no | 22 |
| Slovenia | B | undecided | 11 |
| Canada | A | yes | 29 |
| Canada | A | no | 164 |
| Canada | A | undecided | 40 |
| Kenya | C | yes | 8 |
| Kenya | C | no | 12 |
| Kenya | C | undecided | 0 |
So, in this example, I would like to return from an input title 'Australia:
| Title | average |
| -------- | -------------- |
| Australia| 154 |
| Canada | 159 |
| Turkey | 130 |
My attempted solution is above, I've tried multiple ways to organise the order by, which is what I think is the issue, but I can't get it to work at all. If anyone could help me fix this I'd really appreciate it.
Use conditional aggregation:
WITH cte AS (
SELECT title,
ROUND(SUM(value *
CASE competing
WHEN 'yes' THEN 1
WHEN 'no' THEN 2
WHEN 'undecided' THEN 3
END
) / 3.0) average
FROM tablename
WHERE type = 'A'
GROUP BY title
)
SELECT *
FROM cte
ORDER BY title = 'Australia' DESC,
ABS(average - (SELECT average FROM cte WHERE title = 'Australia'))
LIMIT 3;
See the demo.

SQL Server 2016 count similar rows as a column without duplicating query

I have a SQL query that returns data similar to this pseudo-table:
| Name | Id1 | Id2 | Guid |
|------+-----+-----+------|
| Joe | 1 | 1 | 1123 |
| Joe | 2 | 1 | 1123 |
| Joe | 3 | 1 | 1120 |
| Jeff | 1 | 1 | 1123 |
| Moe | 3 | 42 | 1120 |
I would like to display an additional column on the output, listing the total number of records that have matching GUIDs to a given row, like this:
| Name | Id1 | Id2 | Guid | # Matching |
+------+-----+-----+------+------------+
| Joe | 1 | 1 | 1123 | 3 |
| Joe | 2 | 1 | 1123 | 3 |
| Joe | 3 | 1 | 1120 | 2 |
| Jeff | 1 | 1 | 1123 | 3 |
| Moe | 3 | 42 | 1120 | 2 |
I was able to accomplish this by joining the query with itself, and doing a count. However, the query is rather large and takes awhile to complete, is there any way I can accomplish this without joining the query with itself?
You want a window function:
select t.*, count(*) over (partition by guid) as num_matching
from t;

Creating SQL Query references a few tables

I tried to do this myself with views but I unfortunately I don't have the skills or knowledge required.
I've put in some dummy data to demonstrate.
uri of course = object_uri
TABLE_RECORD
+-----+---------------+---------+
| uri | title | client |
+-----+---------------+---------+
| 1 | australia | peter |
| 2 | new zealand | peter |
| 3 | canada | chris |
| 4 | united states | mitch |
| 5 | ireland | michael |
| 6 | scotland | mitch |
+-----+---------------+---------+
TABLE_UDF
+------------+--------------+----------------+
| object_uri | udf_type_uri | udf_type_value |
+------------+--------------+----------------+
| 1 | 2005 | 1/12/2007 |
| 2 | 2005 | 2/04/2008 |
| 2 | 2006 | 3/04/2009 |
| 3 | 2005 | 4/05/2010 |
| 4 | 2006 | 12/04/2016 |
| 5 | 2005 | 14/05/2005 |
| 5 | 2006 | 14/05/2006 |
| 6 | 2005 | 20/01/2017 |
+------------+--------------+----------------+
EXPECTED OUTPUT
+-----+---------------+---------+------------+------------+
| uri | title | client | udf_type_1 | udf_type_2 |
+-----+---------------+---------+------------+------------+
| 1 | australia | peter | 1/12/2007 | |
| 2 | new zealand | peter | 2/04/2008 | 3/04/2009 |
| 3 | canada | chris | 4/05/2010 | |
| 4 | united states | mitch | | 12/04/2016 |
| 5 | ireland | michael | 14/05/2005 | 14/05/2006 |
| 6 | scotland | mitch | 20/01/2017 | |
+-----+---------------+---------+------------+------------+
Thanks heaps in advanced.
If I understand correctly, this is a join with conditional aggregation:
select r.uri, r.title, r.client,
max(case when u.udf_type_uri = 2005 then udf_type_value end) as udf_type_1,
max(case when u.udf_type_uri = 2006 then udf_type_value end) as udf_type_2
from record r join
udf u
on r.uri = u.object_uri
group by r.uri, r.title, r.client;
SELECT
*
FROM
table_record
LEFT JOIN
(
SELECT
object_uri,
MAX(CASE WHEN udf_type_uri = 2005 THEN udf_type_value END) AS udf_type_1,
MAX(CASE WHEN udf_type_uri = 2006 THEN udf_type_value END) AS udf_type_2
FROM
table_udf
GROUP BY
object_uri
)
table_udf
ON table_udf.object_uri = table_record.uri
The inner query squashes the udf table down to one row per uri, and uses MAX() and CASE to ensure the correct udf is placed in the correct column. Then you just join the tables as normal.
(Can also be done using PIVOT, but that's always seems clunkier to me...)
https://technet.microsoft.com/en-us/library/ms177410(v=sql.105).aspx

SQL - aggregation with column value as column name

For a table like below need to do an aggregation such that for each unique field in one column, need to find the count of occurrences of a discrete value in another column
input table is:
id model datetime driver distance
---|-----|------------|--------|---------
1 | S | 04/03/2009 | john | 399
2 | X | 04/03/2009 | juliet | 244
3 | 3 | 04/03/2009 | borat | 555
4 | 3 | 03/03/2009 | john | 300
5 | X | 03/03/2009 | juliet | 200
6 | X | 03/03/2009 | borat | 500
7 | S | 24/12/2008 | borat | 600
8 | X | 01/01/2009 | borat | 700
Output required
model john juliet | borat
-----|--------|-------|------
S | 1 | 0 | 1
X | 0 | 2 | 2
3 | 1 | 0 | 1
one potential way to do is to group by model with an aggregation like
SUM (CASE WHEN driver = 'value' THEN 1 ELSE 0 END) AS value for each discrete value of driver column. But the challenge is sometimes the number of discrete values is too many ( around 50 in my case) or in some cases do not even know all possible discrete values - I was wondering if there is an alternate way to do this.
The aggregation part need a litle more work.
Here the details:
Need calculate first what are all the combinations
Then use LEFT JOIN to get which combination doesnt have data.
DEMO
WITH "allDrivers" as (
SELECT DISTINCT "driver"
FROM Table1
),
"allModels" as (
SELECT DISTINCT "model"
FROM Table1
),
"source" as (
SELECT d."driver", m."model"
FROM "allDrivers" d
CROSS JOIN "allModels" m
)
SELECT s."model", s."driver", COUNT(t."datetime")
FROM "source" s
LEFT JOIN table1 t
ON s."model" = t."model"
AND s."driver" = t."driver"
GROUP BY s."model", s."driver"
OUTPUT
| model | driver | count |
|-------|--------|-------|
| 3 | borat | 1 |
| 3 | john | 1 |
| 3 | juliet | 0 |
| S | borat | 1 |
| S | john | 1 |
| S | juliet | 0 |
| X | borat | 2 |
| X | john | 0 |
| X | juliet | 2 |
Then you can do the dynamic pivot

Result of cube query shall be null if one element is null

I've got a Cube with one measure, which uses COUNT as aggregation function.
The result of MDX queries looks something like this:
| Germany | USA | Russia | France | Italy |
------------------------------------------------------
2010 | 15 | 20 | null | null | null |
2011 | 20 | 25 | 10 | null | null |
2012 | 25 | 30 | 15 | 5 | null |
2010 - 2012| 60 | 75 | 25 | 5 | null |
For me it works just fine, but our customer wants the whole aggreagtion result to be null if one of the dimension elements is null.
So the result has to look like this:
| Germany | USA | Russia | France | Italy |
------------------------------------------------------
2010 | 15 | 20 | null | null | null |
2011 | 20 | 25 | 10 | null | null |
2012 | 25 | 30 | 15 | 5 | null |
2010 - 2012| 60 | 75 | null | null | null |
And to make things more complicated this behavior should be the same, when the time dimension is put on the slice axis.
So the Result for the following MDX query
SELECT [Area].[Germany]:[Area].[Italy] on 0
FROM ExampleCube
WHERE ([Year].[2010]:[Year].[2012])
should look like this
| Germany | USA | Russia | France | Italy |
------------------------------------------------------
| 60 | 75 | null | null | null |
Exists a way in SSAS and/or MDX to achieve this behavior?