I have had difficulties to sort this simple task out. I'll give you an example of what I would like to do. I want to create a top 5 list in a Report Builder matrix, and then I want to have the "left over" values aggregated and name them Other.
So far I have made a top 5 list within a group, added a new row and made a bottom list of the Other values. I'm not able to aggregate the Other values to one row. This is what I would like to have some help with.
The list could look like this:
Country Value
USA_____10
Canada__9
Mexico___8
Spain____7
France___6
Germany__5
Italy______4
Greece___3
Norway___2
Total_____54
And I would like to present the list like this:
Country Value
USA____10
Canada_9
Mexico__8
Spain___7
France__6
Other___14 *
Total____54
Check this Sql Statement:
Customise your Query
select ROW_NUMBER()over(order by Points desc)ID,* into #Tmp from
(select 'USA'Country,10 Points
union all
select 'Canada',9
union all
select 'Mexico',8
union all
select 'Spain',7
union all
select 'France',6
union all
select 'Germany',5
union all
select 'Italy',4
union all
select 'Greece',3
union all
select 'Norway',2)P
declare #min int=(select min(ID) from #Tmp),
#max int=(select min(ID)+4 from #Tmp)
--select * from #Tmp
select
Case when ID between #min and #max then Cast(ID as varchar)+' '+Country else 'Others' end Sort,
Case when ID between #min and #max then Country else 'Others' end Country,
Sum(Cast(Points as numeric(18,2)))Points
from #Tmp
group by Case when ID between #min and #max then Cast(ID as varchar)+' '+Country else 'Others' end,
Case when ID between #min and #max then Country else 'Others' end
order by Sort
drop table #Tmp
Related
I have a multi fields table with a Countries column. I like the results from a query to be ordered by a particular country first and the rest alphabetically. In MySQL I would do something like:
Select * from myTable Order By Field(Countries,'Italy'),Countries
In Visual-FoxPro I have tried indexing the Cursor created by this query:
Select * from myTable Order By Countries
Index on Countries<>'Italy' TAG test
This would display all results for 'Italy' first, but leave the rest in an unpredictable order.
How to achieve this in Visual-FoxPro?
In VFP you can do it with something like this:
SELECT * FROM myTable WHERE Countries='Italy' ;
UNION ALL ;
SELECT * FROM (SELECT * FROM myTable WHERE Countries<>'Italy' ORDER BY Countries) as secsel
It does order by "if countries is not Italy first then Italy", Countries. Right?
In VFP you can use IIF(). ie:
Select *, iif(Countries == 'Italy', 1, 0) as CItaly ;
from myTable :
Order By CItaly,Countries
Note: If you want to do this via an index then you can use a composite index like:
index on iif(Countries = 'Italy', '1', '0') + Countries tag myCountry
I have a query that I need for it to return a record even when there are no records. In the case where there are records, I simply want those records returned. On the other hand, when there are no records, I need it to still return a record but with the value for the "context" column (the GROUP BY column) equal to the value of the GROUP BY column that did not meet the criteria and a default value for aggregate function/column (e.g., 0). I tried a subquery:
SELECT
(
SELECT
CONTEXT,
SUM(VAL)
FROM
A_TABLE
WHERE
COL = 'absent'
GROUP BY
CONTEXT
)
FROM
DUAL;
but anything greater than one column in the subquery SELECT clause fails w/ a "too many values" message.
I also tried a UNION (with a little more context to more faithfully represent my situation):
SELECT
*
FROM
(
SELECT
CONTEXT,
SUM(VAL)
FROM
A_TABLE
WHERE
COL = 'absent'
GROUP BY
CONTEXT
UNION
SELECT
CONTEXT,
0
FROM
B_TABLE
)
AB_TABLE
INNER JOIN C_TABLE C -- just a table that I need to join to
ON
C.ID = AB_TABLE.C_ID
WHERE
C.ID = 10
AND ROWNUM = 1 -- excludes 2nd UNION subquery result when 1st returns record;
This one does work but I don't know why since the 2nd UNION subquery does not seem to be expressly connected w/ the first (I need the 2nd CONTEXT value to be the same as the 1st for the case where the 1st returns no records). The problem is that the real query does not return any records when I try to implement a similar strategy. I would like to see if there's a better way to approach this problem and perhaps get it to work for the real query (not included as it is too large and somewhat sensitive).
I am not sure I understand the question, but let's try.
I believe what you are saying is this. You have a table called A_TABLE, with columns CONTEXT, VAL, COL (and perhaps others as well).
You want to group by CONTEXT, and get the sum of VAL but only for those rows where COL = 'absent'. Otherwise you want to return a default value (let's say 0).
This can be done with conditional aggregation. The condition is in a CASE expression within the SUM, not in a WHERE clause (as you saw already, if you filter by COL='absent', in a WHERE clause, the query - past the WHERE clause - has no knowledge of the CONTEXT values that don't appear in any rows with COL = 'absent').
If the "default value" was NULL, you could do it like this:
select context, sum(case when col = 'absent' then value end) as val
from a_table
group by context
;
If the default value is anything other than NULL, the temptation may be to use NVL() around the sum. However, if VAL may be NULL, then it is possible that SUM(VAL) is NULL even when there are rows with COL = 'absent'. To address that possibility, you must leave the sum as NULL in those cases, and instead set the value to 0 (or whatever other "default value") only when there are NO rows with COL = 'absent'. Here is one way to do that. Still a standard "conditional" aggregate query:
select context,
case when count(case when col = 'absent' then 1 end) > 0
then sum(case when col = 'absent' then value end)
else 0 -- or whatever "default value" you must assign here
end as val
from a_table
group by context
;
Here's another way you could handle it that avoids the two additional tables (B_TABLE and C_TABLE).
SELECT context
, MAX(val)
FROM (
SELECT context
, SUM(val) as val
FROM a_table
WHERE col = 'absent'
GROUP BY context
UNION
SELECT context
, 0 as val
FROM a_table
) t
GROUP BY context
This assumes the default value you want to return is 0 and that any value in A_TABLE.VAL will be a positive integer.
http://sqlfiddle.com/#!4/c6ca0/20
SELECT b.context
, sum(a.val)
FROM b_table b
LEFT OUTER JOIN a_table a
ON a.context = b.context
AND a.col = 'absent'
GROUP BY b.context
I have this example code of something I'm trying to run. Only he table names and column names were changed. What I want to do is have a result set of states and have 'NULL' be the first value and the rest of the results appear below 'NULL' in ascending order and I can't for the life of me make it work. I get the error at the bottom. This may be a very "noobish" question, but can anyone help? Much appreciated everyone!
SELECT DISTINCT
State
FROM TABLE1 (NOLOCK)
WHERE COLUMN1 NOT LIKE '%THAT%'
AND COLUMN1 NOT LIKE '%THIS%'
UNION
SELECT 'NULL'
ORDER BY ( CASE WHEN State = 'NULL' THEN 0
ELSE 1
END );
Error Message:
ORDER BY items must appear in the select list if the statement contains a UNION, INTERSECT or EXCEPT operator.
You need to select that column... even if you don't use it later, the order by requires it to be present in the select.
SELECT DISTINCT
State,
CASE WHEN State = 'NULL' THEN 0
ELSE 1 END orderId
FROM TABLE1 (NOLOCK)
WHERE COLUMN1 NOT LIKE '%THAT%'
AND COLUMN1 NOT LIKE '%THIS%'
ORDER BY ( CASE WHEN State = 'NULL' THEN 0
ELSE 1
END );
I have two table- 'Order' and 'Order Item'.
Order table contains-
Order Number, Order Date, etc.
Order Item table contains-
Order Number, Order Item Number, Product Name, etc.
The joining condition between these two tables is on Order Number.
In my target table I need orders and a flag. The flag should tell, if there is a predefined set of products which has been ordered as part of that order then it should be set to 'Yes'.
E.g., Suppose an order 'ORD-01' contains three products in Order Item table - 'Mobile', 'PC' and 'Tablet', then my resulting table should contain Order Number as ORD-01 and Flag as 'Yes'.
In the same way, if order 'ORD-02' contains only two prods 'Mobile' an 'Tablet', then the resulting table should contains 'ORD-02' and Flag 'No'.
Similarly, if order 'ORD-03' contains three different prods 'Notebook', 'PC' an 'Tablet', then the resulting table should contains 'ORD-03' and Flag 'No'.
As per my understanding, I have written below query-
SELECT order_number,(SELECT CASE WHEN COUNT(DISTINCT product_name)>=3
THEN 'Yes' ELSE 'No' END Prod_Flag
FROM order_item b
WHERE a.order_number=b.order_number
AND b.product_name IN ('Mobile','PC','Tablet'))
FROM order a
WHERE order_date>last_run_date;
But it takes too much of time, as the order item is a very big table (>1 Billion rows). However I need incremental data based upon order date from Order table. Even if there is an index of order number in both tables, it takes time.
Would a query like this get you to your result any quicker?
SELECT ON.ORDER_NUMBER,
CASE WHEN SET_FOUND.ORDER_NUMBER IS NOT NULL
THEN 'Yes' ELSE 'No' END PROD_FLAG
FROM ORDER ON,
(SELECT ORDER_NUMBER
FROM ORDER_ITEM
WHERE PRODUCT_NAME = 'Mobile'
INTERSECT
SELECT ORDER_NUMBER
FROM ORDER_ITEM
WHERE PRODUCT_NAME = 'PC'
INTERSECT
SELECT ORDER_NUMBER
FROM ORDER_ITEM
WHERE PRODUCT_NAME = 'Tablet') SET_FOUND
WHERE ON.ORDER_NUMBER = SET_FOUND.ORDER_NUMBER (+)
My proposal would be this one:
WITH t AS
(SELECT product_name, order_number
FROM order_item
WHERE product_name IN ('Mobile','PC','Tablet')
GROUP BY order_number, product_name)
SELECT order_number,
CASE WHEN COUNT(DISTINCT product_name) >= 3 THEN 'Yes' ELSE 'No' END
FROM t
JOIN order USING (order_number)
GROUP BY order_number
Is the order number an increasing sequence number? If so the one approach would be to limit data selected from the order_item, which you said is a large table, by putting condition on order_number, which you said is an indexed column. I assume last_run_date signifficantly limits number of concerned orders.
If so you can:
select min(order_number) into order_num_from from Order where order_date>last_run_date
and then make your query
SELECT order_number,(SELECT CASE WHEN COUNT(DISTINCT product_name)>=3
THEN 'Yes' ELSE 'No' END Prod_Flag
FROM order_item b
WHERE a.order_number=b.order_number
AND b.order_number> order_num_from
AND b.product_name IN ('Mobile','PC','Tablet'))
FROM order a
WHERE order_date>last_run_date;
If this runs significantly faster (I didn't see explain plan, so this is just an idea how to avoid full table scan ), put an index on order_date column and eventually make finding order_num_from into subquery to have one single query.
Generally, your query is right. As I understood, you wish to raise it's speed. If so, there are several ways you can try.
You can consider to put these tables into indexed cluster. It will store the data physically joined so querying would require less physical reads.
For this query, server should scan two tables: one for appropriate dates (eigther full table scan or index scan), other for products and joins the results by reading ORDER_NUMBER via rowid. It isn't very fast anyway. The simpliest way is to add (ORDER_DATE, ORDER_NUMBER) index for ORDERs and (ORDER_NUMBER, PRODUCT_NAME) index for ORDER_ITEMs; it will allow to use indexes only.
Maybe it would be suitable to make a fast-refreshable materialized view, something like
create materialized view as
select
a.order_date,
a.order_number,
sum(case when b.product_name = 'Mobile' then 1 else 0 end) cnt_mobiles,
sum(case when b.product_name = 'PC' then 1 else 0 end) cnt_pcs,
sum(case when b.product_name = 'Tablet' then 1 else 0 end) cnt_tablets
from
order a, order_item b
where
a.order_number = b.order_number
group by
a.order_number, a.order_date
If it would be impossible to make this fast-refreshable, you can do equal thing manually using trigger. Anyway, in this case you'll get precalculated data ready to check.
I have a requirement where in if a PO_ID range is given then I need to do the following:
If a PO_ID from the PO_ID range is present in PS_DISTRIB_LINE then print PO_ID and Voucher ID and if not then print PI_ID and PO_DT from PS_PO_HDR.
How to achieve this.
Union is not working and I am not able to use break logic because if data is present in PS_DISTRIB_LINE then I am printing Sum of PO_AMT Total below the PO_IDs but in other case i am not.
UNION is possible in SQR, but the syntax is difficult.
You will need to put the UNION in parenthesis and make sure to put commas after the selected items. In the example below, I put commas after a.name, a.first_name, and a.last_name. Repeat this for b.name, b.first_name, and b.last_name.
I've run this example and this works for me:
begin-SELECT
employee_name.name
employee_name.first_name
employee_name.last_name
Show 'employee_name.name: ' &employee_name.name
Show 'employee_name.first_name: ' &employee_name.first_name
Show 'employee_name.last_name: ' &employee_name.last_name
from
(
select
a.name,
a.first_name,
a.last_name
from ps_names a
where name_type = 'PRI'
and emplid = '000000001'
union
select
b.name,
b.first_name,
b.last_name
from ps_personal_data b
where emplid = '000000001'
) employee_name
end-SELECT