Distinct on row level - sql-server-2008

It seemed so easy.
I am getting following table by using COALESCE. I need to perform distinct on row level.
1 1 5 5 5 (null)
2 2 2 2 25 25
3 7 35 35 35 35
That's what I am looking for.
1 5 null
2 25
3 7 35
Here's a Demo on http://sqlfiddle.com/#!3/e945b/5/0

This is the only way I can think of doing it.
Do not currently have enough time to explain its operation, so please post questions in comments;
WITH DataCTE (RowID, a, b, c, d, e, f) AS
(
SELECT 1, 1, 1, 5, 5, 5, NULL UNION ALL
SELECT 2, 2, 2, 2, 2, 25, 25 UNION ALL
SELECT 3, 3, 7, 35, 35, 35, 35
)
,UnPivotted AS
(
SELECT DC.RowID
,CA.Distinctcol
,OrdinalCol = ROW_NUMBER() OVER (PARTITION BY DC.RowID ORDER BY CA.Distinctcol)
FROM DataCTE DC
CROSS
APPLY (
SELECT Distinctcol
FROM
(
SELECT Distinctcol = a UNION
SELECT b UNION
SELECT c UNION
SELECT d UNION
SELECT e UNION
SELECT f
)DT
WHERE Distinctcol IS NOT NULL
) CA(Distinctcol)
)
SELECT RowID
,Col1 = MAX(CASE WHEN OrdinalCol = 1 THEN Distinctcol ELSE NULL END)
,Col2 = MAX(CASE WHEN OrdinalCol = 2 THEN Distinctcol ELSE NULL END)
,Col3 = MAX(CASE WHEN OrdinalCol = 3 THEN Distinctcol ELSE NULL END)
,Col4 = MAX(CASE WHEN OrdinalCol = 4 THEN Distinctcol ELSE NULL END)
,Col5 = MAX(CASE WHEN OrdinalCol = 5 THEN Distinctcol ELSE NULL END)
FROM UnPivotted
GROUP BY RowID

Related

SQL Query to create a view with different grouping logic

I have a table similar to below
Insurance ID
Created By
Closed By
1
User A
User A
2
User A
User C
3
User B
User C
4
User B
User C
5
User B
User C
From this table, I am trying to create a View as below
UserName
Total Created
Total Closed
User A
2
1
User B
3
0
User C
0
4
I am not able to figure out how to group the table to achieve this view. Any help would be greatly appreciated
Here's one option:
Sample data (you have it already, so you don't type that):
SQL> with test (insurance_id, created_by, closed_by) as
2 (select 1, 'user a', 'user a' from dual union all
3 select 2, 'user a', 'user c' from dual union all
4 select 3, 'user b', 'user c' from dual union all
5 select 4, 'user b', 'user c' from dual union all
6 select 5, 'user b', 'user c' from dual
7 ),
Query begins here:
8 all_users as
9 (select created_by username from test
10 union
11 select closed_by from test
12 )
13 select u.username,
14 sum(case when t.created_by = u.username then 1 else 0 end) total_created,
15 sum(case when t.closed_by = u.username then 1 else 0 end) total_closed
16 from all_users u cross join test t
17 group by u.username
18 order by u.username;
USERNA TOTAL_CREATED TOTAL_CLOSED
------ ------------- ------------
user a 2 1
user b 3 0
user c 0 4
SQL>
I would be inclined to have a separate Users table and only have an integer of UserId in the main table. CROSS APPLY should avoid reading the same table twice.
SELECT X.UserName
,SUM(CASE WHEN X.Activity = 'Created' THEN 1 ELSE 0 END) AS TotalCreated
,SUM(CASE WHEN X.Activity = 'Closed' THEN 1 ELSE 0 END) AS TotalClosed
FROM YourTable T
CROSS APPLY
(
VALUES (T.CreatedBy, 'Created')
,(T.ClosedBy, 'Closed')
) X (UserName, Activity)
GROUP BY X.UserName
ORDER BY UserName;
create table sometable (user_id, created_by,closed_by)
as
select 1, 'user a', 'user a' from dual union all
select 2, 'user a', 'user c' from dual union all
select 3, 'user b', 'user c' from dual union all
select 4, 'user b', 'user c' from dual union all
select 5, 'user b', 'user c' from dual;
SELECT *
FROM sometable
UNPIVOT ( username
FOR col IN ( created_by
, closed_by
)
)
PIVOT ( COUNT (user_id)
FOR col IN ( 'CREATED_BY' AS total_created
, 'CLOSED_BY' AS total_closed
)
)
ORDER BY username
;
USERNAME TOTAL_CREATED TOTAL_CLOSED
user a 2 1
user b 3 0
user c 0 4

CASE WHEN IN ({set of numbers})

I need a following case statement in MySQL.
When column value is (1, 2, 5, 7, 14, 17) - return 0, otherwise return 1 and I need to use it in order by clause.
My first impression was to make query like this:
SELECT ... ORDER BY (CASE column WHEN IN (1, 2, 5, 7, 14, 17) THEN 0 ELSE 1 END) DESC
but this obviously fails.
I can write it like this:
SELECT ... ORDER BY (CASE column WHEN 1 THEN WHEN 2 THEN 0 WHEN 5 THEN ... 0 ELSE 1 END) DESC
But I am looking for a more elegant way. Is there any other elegant syntax?
This needs to work
SELECT ...
ORDER BY
CASE WHEN (column IN (1, 2, 5, 7, 14, 17) THEN 0 ELSE 1 END) DESC
Demonstration:
with cte0 as
(
select 120 x from dual union
select 1 from dual union
select 22 from dual union
select 7 from dual
)
select * from cte0
order by (case when x in (22) then 0 else 1 end) desc;
| X |
| --: |
| 1 |
| 120 |
| 7 |
| 22 |
db<>fiddle here
You can use nested query, e.g.:
SELECT B.*
FROM (
SELECT A, CASE WHEN B IN (1, 2, 5, 7, 14, 17) THEN 1 ELSE 0 END AS ORDERING
FROM TABLE
) B
ORDER BY B.ORDERING DESC;

Why MySQL full outer join returns nulls?

Why MySQL full outer join returns nulls?
Hi
I have the following data:
s_id,date,p_id,amount_sold
1, '2015-10-01', 1, 10
2, '2015-10-01', 2, 12
7, '2015-10-01', 1, 11
3, '2015-10-02', 1, 11
4, '2015-10-02', 2, 10
5, '2015-10-15', 1, 22
6, '2015-10-16', 2, 20
8, '2015-10-22', 3, 444
and i want my query to output something like this: (A = sum of amount_sold for p_id=1 for that date,B = sum of amount_sold for p_id=2 for that date)
date,A,B,Difference
'2015-10-01',21,12,9
'2015-10-02',11,10,1
'2015-10-15',22,0,22
'2015-10-01',0,20,-20
I tried with this query, but the order its returning is having NULLS and the output is wrong:
SELECT A.p_id,A.date,sum(A.amount_sold) A,B.Bs, (sum(A.amount_sold) - B.Bs) as difference FROM sales as A
LEFT JOIN (
SELECT SUM( amount_sold ) Bs,p_id,s_id, DATE
FROM sales
WHERE p_id =2
group by date
) as B ON A.s_id = B.s_id
where A.p_id=1 or B.p_id=2
group by A.date, A.p_id
UNION
SELECT A.p_id,A.date,sum(A.amount_sold) A,B.Bs, (sum(A.amount_sold) - B.Bs) as difference FROM sales as A
RIGHT JOIN (
SELECT SUM( amount_sold ) Bs,p_id,s_id, DATE
FROM sales
WHERE p_id =2
group by date
) as B ON A.s_id = B.s_id
where B.p_id=2
group by A.date, A.p_id
It returned:
p_id date A Bs difference
1 2015-10-01 21 NULL NULL
2 2015-10-01 12 12 0
1 2015-10-02 11 NULL NULL
2 2015-10-02 10 10 0
1 2015-10-15 22 NULL NULL
2 2015-10-16 20 20 0
What am i doing wrong here? and what is the correct way of doing it? any help would be appreciated.
A full join isn't needed. You can use conditional aggregation instead:
select
date,
sum(case when p_id = 1 then amount_sold else 0 end) a,
sum(case when p_id = 2 then amount_sold else 0 end) b,
sum(case when p_id = 1 then amount_sold else 0 end)
- sum(case when p_id = 2 then amount_sold else 0 end) difference
from sales
where p_id in (1,2)
group by date

Left JOIN returning ALL NULL values when no corresponding row in right table

I have the following query and the sub query is producing a strange result when left joined. Here is what is happening (row with StoreID 1 is returning all null in joined table) and what I really want :
TABLE A
StoreID unitsales1 unitsales2 unitsales3
1 NULL NULL 6
2 12 12 12
3 12 NULL 12
TABLE B
StoreID prevunitsales1 prevunitsales2 prevunitsales3
2 NULL NULL 6
3 12 12 12
What I am getting
LEFT JOIN ON A.StoreId = B.StoreId
StoreID unitsales1 unitsales2 unitsales3 prevunitsales1 prevunitsales2 prevunitsales3
1 NULL NULL NULL NULL NULL NULL
2 12 12 12 NULL NULL 6
3 12 NULL 12 12 12 12
What I want
LEFT JOIN ON A.StoreId = B.StoreId
StoreID unitsales1 unitsales2 unitsales3 prevunitsales1 prevunitsales2 prevunitsales3
1 NULL NULL 6 NULL NULL NULL
2 12 12 12 NULL NULL 6
3 12 NULL 12 12 12 12
Here is the query:
SELECT SQL_CALC_FOUND_ROWS #storeid:=z.id,z.biz_name, z.wf_store_name, z.e_address, z.e_city, z.e_state, z.e_postal, IFNULL(total_sales - prev_total_sales,'CV') as diff_total_sales, IFNULL(d_source,'N/A') as d_source, IFNULL(unit_sales1 - prev_unit_sales1,(SELECT IFNULL(max(datetimesql),'NS') FROM storeCheckRecords WHERE store_id=#storeid AND upc=855555000019)) as diff_unit_sales1, IFNULL(unit_sales2 - prev_unit_sales2,(SELECT IFNULL(max(datetimesql),'NS') FROM storeCheckRecords WHERE store_id=#storeid AND upc=855555000022)) as diff_unit_sales2, IFNULL(unit_sales3 - prev_unit_sales3,(SELECT IFNULL(max(datetimesql),'NS') FROM storeCheckRecords WHERE store_id=#storeid AND upc=855555000025)) as diff_unit_sales3, IFNULL(unit_sales4 - prev_unit_sales4,(SELECT IFNULL(max(datetimesql),'NS') FROM storeCheckRecords WHERE store_id=#storeid AND upc=855555000028)) as diff_unit_sales4 FROM
(SELECT s1.id,s1.biz_name as biz_name, s1.wf_store_name as wf_store_name, s1.e_address as e_address,s1.e_city as e_city,s1.e_state as e_state,s1.e_postal as e_postal,sum(s2.unit_sales) as total_sales, sum(s2.unit_sales/4.28571428571) as week_avg,group_concat(DISTINCT s2.d_source separator ',') as d_source,
SUM(CASE u.id WHEN 1 THEN s2.unit_sales ELSE NULL END) AS unit_sales1,SUM(CASE u.id WHEN 2 THEN s2.unit_sales ELSE NULL END) AS unit_sales2,SUM(CASE u.id WHEN 3 THEN s2.unit_sales ELSE NULL END) AS unit_sales3,SUM(CASE u.id WHEN 4 THEN s2.unit_sales ELSE NULL END) AS unit_sales4
FROM allStores as s1
INNER JOIN storeCheckRecords AS s2
ON s1.id = s2.store_id
AND s2.datetimesql BETWEEN '2015-07-01' AND '2015-07-31'
AND s1.key_retailer LIKE 'FRESH THYME'
INNER JOIN ( SELECT 1 AS id, '855555000019' AS upc UNION SELECT 2, '855555000022' UNION SELECT 3, '855555000025' UNION SELECT 4, '855555000028' ) u ON u.upc = s2.upc
GROUP BY
s1.id) x
LEFT OUTER JOIN
(SELECT s1.id,s1.biz_name as prev_biz_name, s1.wf_store_name as prev_wf_store_name, s1.e_address as prev_e_address,s1.e_city as prev_e_city,s1.e_state as prev_e_state,s1.e_postal as prev_e_postal,sum(s2.unit_sales) as prev_total_sales, sum(s2.unit_sales/4.28571428571) as prev_week_avg,group_concat(DISTINCT s2.d_source separator ',') as prev_d_source,
SUM(CASE u.id WHEN 1 THEN s2.unit_sales ELSE 0 END) AS prev_unit_sales1,SUM(CASE u.id WHEN 2 THEN s2.unit_sales ELSE 0 END) AS prev_unit_sales2,SUM(CASE u.id WHEN 3 THEN s2.unit_sales ELSE 0 END) AS prev_unit_sales3,SUM(CASE u.id WHEN 4 THEN s2.unit_sales ELSE 0 END) AS prev_unit_sales4
FROM allStores as s1
INNER JOIN storeCheckRecords AS s2
ON s1.id = s2.store_id
AND s2.datetimesql BETWEEN '2015-06-01' AND '2015-06-30'
AND s1.key_retailer LIKE 'FRESH THYME'
INNER JOIN ( SELECT 1 AS id, '855555000019' AS upc UNION SELECT 2, '855555000022' UNION SELECT 3, '855555000025' UNION SELECT 4, '855555000028' ) u ON u.upc = s2.upc
GROUP BY
s1.id) y
ON x.id = y.id
RIGHT JOIN
(SELECT s1.id,s1.biz_name,s1.wf_store_name,s1.e_address,s1.e_city,s1.e_state,s1.e_postal
FROM allStores as s1
WHERE 1
AND s1.key_retailer LIKE 'FRESH THYME') z
ON y.id = z.id
ORDER BY biz_name ASC
LIMIT 0, 1000
What am I missing?
Comparing NULL with NULL using == never matches, which is why there is no table2 row that matches the first row of table1.
You can use <==> instead if you really want to do this.
mysql> SELECT 1 <=> 1, NULL <=> NULL, 1 <=> NULL;
-> 1, 1, 0
mysql> SELECT 1 = 1, NULL = NULL, 1 = NULL;
-> 1, NULL, NULL

SQL Query to get the total sales per seller by month

I try this:
SELECT
profile.user_id,
profile.name,
total_month.total as month10
FROM profile
LEFT OUTER JOIN (SELECT
order.seller_id,
COUNT(*) AS total
FROM order
WHERE MONTH(order.data_hora) = 10
GROUP BY order.seller_id) AS total_month
ON total_month.seller_id= profile.user_id;
The result was this:
-------------------------
|user_id| name |month10|
-------------------------
| 5 |user1 | 73 |
| 1 |user2 | 1 |
-------------------------
But I need more months like this:
-------------------------------------------------
| user_id | name | month10 | month11 | month12 |
-------------------------------------------------
| 5 | user1 | 73 | 52 | 65 |
| 1 | user2 | 67 | 56 | 78 |
-------------------------------------------------
How could I do this without creating a function?
You can extend your query to do what you want. Just be more flexible in the subquery:
SELECT p.user_id, p.name,
tm.month10, tm.month11, tm.month12
FROM profile p LEFT OUTER JOIN
(SELECT o.seller_id,
sum(o.data_hora = 10) as month10,
sum(o.data_hora = 11) as month11,
sum(o.data_hora = 12) as month12
FROM order o
WHERE MONTH(order.data_hora) in (10, 11, 12)
GROUP BY order.seller_id
) tm
ON tm.seller_id = p.user_id;
Well as I said in the comments supporting the #Schalk comment, in order to get this working as you want you will need a function to get a DYNAMIC PIVOT TABLE or TRANSPOSE ROWS TO COLUMNS google it if you prefer.
For your solution I've created a query that gives you all months/values like this:
user_id name jan feb mar apr may jun jul aug sep oct nov dec
1 a 0 0 1 10 0 0 1 2 7 2 3 0
2 b 1 0 0 0 2 0 0 0 3 1 1 0
If it fits to your problem this is your query:
select
profile.user_id,
profile.name,
sum(if( MONTH(orderr.data_hora) = 1, 1, 0 )) as Jan,
sum(if( MONTH(orderr.data_hora) = 2, 1, 0 )) as Feb,
sum(if( MONTH(orderr.data_hora) = 3, 1, 0 )) as Mar,
sum(if( MONTH(orderr.data_hora) = 4, 1, 0 )) as Apr,
sum(if( MONTH(orderr.data_hora) = 5, 1, 0 )) as May,
sum(if( MONTH(orderr.data_hora) = 6, 1, 0 )) as Jun,
sum(if( MONTH(orderr.data_hora) = 7, 1, 0 )) as Jul,
sum(if( MONTH(orderr.data_hora) = 8, 1, 0 )) as Aug,
sum(if( MONTH(orderr.data_hora) = 9, 1, 0 )) as Sep,
sum(if( MONTH(orderr.data_hora) = 10, 1, 0 )) as Oct,
sum(if( MONTH(orderr.data_hora) = 11, 1, 0 )) as Nov,
sum(if( MONTH(orderr.data_hora) = 12, 1, 0 )) as Dece
from
profile left join orderr
on profile.user_id = orderr.seller_id
group by profile.user_id,
profile.name
I've created a fiddle to it (but the data_hora column I created as integer to make it quickly to do it, it is for understanding).
http://sqlfiddle.com/#!2/4a1a2e/5
It was perfect!
The field type 'data_hora' is datetime, so I made a small change.
SELECT p.user_id,
p.name,
tm.month10,
tm.month11,
tm.month12,
(tm.month10+tm.month11+tm.month12) AS final_total
FROM profile p
LEFT OUTER JOIN
(SELECT o.seller_id,
sum(month(o.data_hora) = 10) AS month10,
sum(month(o.data_hora) = 11) AS month11,
sum(month(o.data_hora) = 12) AS month12
FROM order o
WHERE MONTH(ORDER.data_hora) IN (10, 11, 12)
GROUP BY ORDER.seller_id ) tm
ON tm.seller_id = p.user_id
ORDER BY final_total DESC;
How could I optimize the field "final_total"?