Calculate Median with condition on Partition By columns - sql-server-2012

I need to calculate median for Items with label as "Tag" and group with similar Item_Names such as C, E and F. E.g. Median for "Tag C1", "Tag C2" and "Tag C3". For the rest of the items the Median is to be calculated on the basis of Label and Item_Name.
Label || Item_Name || Price
Tag || Tag C1 || 231
Tag || Tag C2 || 312
Tag || Tag C3 || 416
Tag || Tag E1 || 523
Tag || Tag E2 || 152
Tag || Tag E3 || 629
Tag || Tag E4 || 29
Tag || Tag E5 || 727
Tag || Tag F1 || 671
Tag || Tag F2 || 1002
Tag || Tag F3 || 96
No_Tag || G || 61
No_Tag || H || 802
No_Tag || H2 || 36
No_Tag || J3 || 102
No_Tag || K || 546
No_Tag || M || 238
The below-mentioned query returns Median over the whole set of items
PERCENTILE_CONT(.50) WITHIN GROUP (ORDER BY Price) OVER (PARTITION BY Label) med_price
I tried with CASE as well but it still gives the same result
CASE
WHEN Item_Name LIKE ('Tag C%') THEN PERCENTILE_CONT(.50) WITHIN GROUP (ORDER BY Price) OVER (PARTITION BY Label)
WHEN Item_Name LIKE ('Tag E%') THEN PERCENTILE_CONT(.50) WITHIN GROUP (ORDER BY Price) OVER (PARTITION BY Label)
WHEN Item_Name LIKE ('Tag F%') THEN PERCENTILE_CONT(.50) WITHIN GROUP (ORDER BY Price) OVER (PARTITION BY Label)
ELSE PERCENTILE_CONT(.50) WITHIN GROUP (ORDER BY Price) OVER (PARTITION BY Label, Item_Name)
END AS med_price,
How do I include a condition based on "Item_Name" while partitioning over "Label"?

You could use the case statement on "Item_Name" - this is a suggestion based on the first five characters of Item_Name being your criteria:
PERCENTILE_CONT(.50) WITHIN GROUP (ORDER BY Price) OVER (PARTITION BY Label, CASE WHEN Label = 'Tag' THEN Left(Item_Name,5) ELSE Item_Name END) AS Med_Price

Related

How can I make an sql query that subtracts times from different rows?

I have the following dataset showing the status of certain tools.
id || Toolid || time || message || timeToComplete
===========================================================
1 || 1 || 1578294000 || running || 153
2 || 1 || 1578294153 || assistance || null
3 || 1 || 1578294204 || done || null
4 || 1 || 1578294264 || running || 208
5 || 1 || 1578294472 || assistance || null
6 || 1 || 1578294524 || done || null
7 || 2 || 1578294584 || running || 127
8 || 2 || 1578294711 || assistance || null
9 || 2 || 1578294772 || done || null
I need this dataset to calculate the response time, but I can't find a query that succesfully substracts the rows of message=done minus message=assistance.
The output needs to look like this:
Toolid || time || timeToComplete || responseTime
================================================
1 || 1578294000 || 153 || 51
1 || 1578294264 || 208 || 52
2 || 1578294584 || 127 || 61
Another challenge is that the assistance-message and done-message aren't always exactly 1 apart. They do always arrive in the same order from the tools (running-assistance-done).
Can someone help me with the necessary query?
I think you want conditional aggregation -- but you need a grouping. You can calculate this by adding up the number of "running" messages for each row:
select toolid,
min(case when message = 'running' then time end) as time,
min(timeToComplete) as timeToComplete,
(min(case when message = 'done' then time end) -
min(case when message = 'assistance' then time end)
) as responseTime
from (select t.*,
sum(case when message = 'running' then 1 else 0 end) over (partition by toolid order by id) as grp
from t
) t
group by toolid, grp;
You could use window min()s to get the next "assistance" and "done" rows :
select tool_id, time, time_to_complete, response_time
from(
select
t.*,
min(case when message = 'done' then time end) over(
partition by tool_id
order by time
rows between 1 following and unbounded following
)
- min(case when message = 'assistance ' then time end over(
partition by tool_id
order by time
rows between 1 following and unbounded following
) response_time
from mytable t
) t
where message = 'running'

Need sql query help to find profile contribution?

I have two tables one for user profile(mls_user) and second for user activity(mls_entry).
The following table(mls_user) is for profile user where i am storing all the information of users.
=====================================================
|| user_id || user_name || user_role || user_email ||
=====================================================
|| 140 || santosh || 2 ||san#mail.com||
|| 150 || Deepak || 2 ||dep#mail.com||
|| 152 || sandeep || 2 ||sad#mail.com||
=====================================================
i have the second table(mls_entry) where i am storing user activity like below:
=======================================================
|| id || user_id || category || Distance || status ||
|| 1 || 140 || running || 10 || approved ||
|| 2 || 140 || running || 20 || approved ||
|| 3 || 140 || cycling || 40 || rejected ||
|| 4 || 140 || cycling || 20 || approved ||
|| 5 || 150 || running || 15 || approved ||
|| 6 || 152 || cycling || 50 || approved ||
=======================================================
Now I want:
==================================================================
|| user_name || runing_entry || cycling_entry || total_distance ||
==================================================================
|| santosh || 2 || 2 || 50 ||
|| deepak || 1 || 1 || 15 ||
|| sandeep || 1 || 1 || 50 ||
==================================================================
So i am not able to find the solution for this. I have tried the following query which i try but result showing wrong.
SELECT mls_user.name,
(CASE
WHEN mls_entry.category='running'
THEN SUM(mls_entry.category='running')
ELSE 0 END) AS runnerEntry,
(CASE
WHEN mls_entry.category='cycling'
THEN SUM(mls_entry.category='cycling')
ELSE 0 END) AS cyclingEntry,
(CASE
WHEN SUM(mls_entry.distance) AND mls_entry.category='running' AND mls_entry.status='approved'
THEN SUM(mls_entry.distance) ELSE 0 END) AS total_point_runner FROM mls_user LEFT JOIN mls_entry ON mls_user.id = mls_entry.user_id GROUP BY mls_user.id HAVING mls_user.role = 2
I would go for conditional aggregation:
select u.user_name, u.user_id
sum(case when category = 'running' then 1 else 0 end) as running,
sum(case when category = 'cycling' then 1 else 0 end) as running,
sum(case when status = 'approved' then distance else 0 end) as distance
from mls_user u join
mls_entry e
on e.user_id = u.user_id
group by u.user_name, u.user_id;
Try this
Select user_name,user_id
,(Select Count(ID) From mls_entry Where mls_entry.user_id=mls_user.user_id AND category='running' As X) As runing_entry
,(Select Count(ID) From mls_entry Where mls_entry.user_id=mls_user.user_id AND category='cycling' As Y) As cycling_entry
,(Select SUM(total_distance) From mls_entry Where mls_entry.user_id=mls_user.user_id As Z) As total_distance
From mls_user

How to get the previous Id value for a given table?

I have two input tables:
1) Site:
site_id|| site_name|| site_location
1000 || abc || XYZ_123
1001 || tyu || ERD_123
1002 || iok || FTR_678
1003 || okn || YHU_987
1004 || ybg || OLP_008
1005 || qwe || PLM_126
2)
product:
Product_id|| product_name||start_date||end_date
212 || sme1 ||2014-12-25||2017-03-13
250 || try1 ||2013-12-15|| 2017-03-13
267 || inu1 || 2015-03-27|| 2017-03-17
I need to check how many times the id is repeated and order it like the output table.
The previousId is to be caluclated if the preoduct_id is repeated and its less than the end date.
This is what I have done 'till now.
select top 1 d2.PRODUCT_id, d2.last_date, d1.*
from output_table d1,
output_table d2
where d1.SITE_id = d2.Site_id
and d1.START_DATE >= d2.end_DATE
and d1.Site_id=1001
and d1.PRODUCT_id = 250
order by d2.End_date desc
any help is appreciated.
i need to output as my table.
this is my output table structure :
Id|| site_id|| product_id|| previous_id|| start_date|| end_date ||Previous_site_id || repeated_times || Previous_id
1 || 1000 ||250 || null || 2015-01-01||2017-03-13 || 1001 ||3 || 2
2 || 1001 ||250 || 1 || 2014-12-25||2015-01-01 || 1002 ||3 || 3
3 || 1002 ||250 || 2 || 2013-12-15||2014-12-25 || Null ||3 || Null
4 || 1003 ||267 || null || 2015-03-27|| 2017-03-17|| Null ||0 || Null
5 ||1004 ||212 || null || 2016-01-01||2017-03-13 ||1004 ||2 || 6
6 || 1005 ||212 || 1 || 2014-12-25||2015-12-30 ||Null ||2 || Null
Since you provided a pretty poor description of your schema, I've made a lot by guess. Considering you use MSSQL 2012, there should be LAG windowed function that could be used in some way similar to the following:
WITH CountedResult AS
(
SELECT
ROW_NUMBER() OVER(ORDER BY product_id, site_id) AS Id, -- use some id instead, if you have
site_id,
product_id,
[start_date],
[end_date],
COUNT(*) OVER(PARTITION BY product_id) AS repeated_times
FROM output_table
WHERE
site_id=1001
AND product_id = 250
)
SELECT
Id, site_id, product_id, [start_date], [end_date],
LAG(site_id, 1, 0) OVER (PARTITION BY product_id ORDER BY [start_date] DESC) AS previous_site_id,
repeated_times,
LAG(Id, 1, 0) OVER (PARTITION BY product_id ORDER BY [start_date] DESC) AS previous_id,
FROM CountedResult
ORDER BY product_id, site_id
Read more about LAG on MSDN: https://msdn.microsoft.com/en-us/library/hh231256.aspx

Multiple Pivots SQL SERVER 2008 on multiple columns

This is my current dataset
select sampleId, plateID, target, confidence, zscore from Observations
sampleId || plateID || target || confidence || zscore
-----------------------------------------------------------------
FR08512332 || 13053620 || I6-2D6 || < 0.50 || 0.84
FR08512332 || 13053620 || E9-2D6 || > 0.99 || 0.05
FR08512332 || 13053620 || I2-2D6 || 0.99 || 0.23
This is my dataset after one pivot to collapse the confidence column
SELECT *
FROM
(
SELECT sampleId, plateID, target, confidence, zscore
from Observations
) S
PIVOT( max(confidence) for target IN ([E9-2D6],[I6-2D6],[I2-2D6])) As confidence
sampleId || plateID || Zscore || e9 || i6 || i2
-------------------------------------------------------------------
FR08512332 || 13053620 || 0.02 || > 0.99 || NULL || NULL
FR08512332 || 13053620 || 0.25 || NULL || NULL || 0.99
FR08512332 || 13053620 || 0.13 || NULL || < 0.55 || NULL
This is the result i am looking to achieve
sampleId || plateID || conf-e9 || conf-i6 || conf-i2 || z-e9 || z-i6 || z-i2
-------------------------------------------------------------------------------------
FR08512332|| 13053620 || > 0.99 || < 0.55 || 0.99 || 0.02 || 0.25 || 0.13
You can use conditional aggregation (easier to understand) instead of pivot as the number of targets are fixed.
Fiddle with sample data
select sampleid, plateid,
max(case when target = 'I6-2D6' then confidence end) as conf_i6,
max(case when target = 'E9-2D6' then confidence end) as conf_e9,
max(case when target = 'I2-2D6' then confidence end) as conf_i2,
max(case when target = 'I6-2D6' then zscore end) as z_i6,
max(case when target = 'E9-2D6' then zscore end) as z_e9,
max(case when target = 'I2-2D6' then zscore end) as z_i2
from observations
group by sampleid, plateid

SQL Join with duplicate row if value in column is more than 1

I have a two tables. First contains documents and details information and the second holds the position of this documents. I want join this table so that if in table2 the quantity is more than 1 I get that number of row in the results. Example below.
Example.
1 Table:
ID_DOC || NR_DOC || YEAR || DATE || COUNTRY
123 || WZ-20 || 2015 ||20150129|| PL
124 || WZ-22 || 2015 ||20150128|| DE
2 Table:
ID_PAL || TYPE_P || QUA || SUMWEIGHT || ID_DOC
111 || EURO || 1 ||200 || 123
112 || EURO || 3 ||900 || 124
Result:
ID_DOC || NR_DOC|| YEAR || DATE || COUNTRY || TYPE_P || QUA ||SUMW
123 || WZ-20 || 2015 ||20150129|| PL || EURO || 1 ||200
124 || WZ-22 || 2015 ||20150129|| PL || EURO || 1 ||300 124 || WZ-22 || 2015 ||20150129|| PL || EURO || 1 ||300 124 || WZ-22 || 2015 ||20150129|| PL || EURO || 1 ||300
It is possible? Thanks for advice.
I've assumed that you want the same number of rows in the results as the value in the Qty column, in which case you can do this with a numbers table - a table that contains a single int column populated with a lot of numbers. Then you can do a join like this
SELECT * FROM [dbo].[Table2] PP
INNER JOIN [dbo].[Table1] DD on PP.docid = DD.docid
INNER JOIN Numbers NN on PP.qty >= NN.n
A quick way to create a numbers table:
CREATE TABLE dbo.Numbers
(
  n INT PRIMARY KEY
);
INSERT dbo.Numbers(n)
SELECT TOP (10000) rn = ROW_NUMBER() OVER
(ORDER BY s1.[object_id])
FROM sys.all_objects AS s1
CROSS JOIN sys.objects AS s2;
select tbl1.id_doc, tbl1.nr_doc, tbl1.year, tbl1.date, tbl1.country, tbl2.type_p, tbl2.qua.tbl2.sumweight
from doc table1, location table2
where table1.id_doc = table2.id_doc
and (select count(*) from location where id_doc = table2.id_doc ) > 1