how to get alphabetically next and prev records wiht minimal fetched records? - sorting

I have a page that is displaying a company name and its details from a table A.
Now say i have a company displayed,and its name is 'Company_one' now i want to have alphabetically sorted next company and previous company and their details etc.
The data in my table is not sorted.Its stored as it gets the data.
So now what kind of query should i write that it gives only one previous and one next alphabetically sorted record??
Plz help!!

There's no nice way to do that in a single query. Just do two queries.
To get the previous one:
SELECT * FROM companies
WHERE name < variable_with_current_name
ORDER BY name DESC
LIMIT 1
To get the next one along:
SELECT * FROM companies
WHERE name > variable_with_current_name
ORDER BY name ASC
LIMIT 1

You need to use the sort clause to sort your table. The prototype for sort is:
sort by fieldname
Example Query:
select * from your_table sort by company asc
If you want to limit records, use limit clause:
select * from your_table sort by company asc limit 0, 1

Based on Dominic's answer, you can achieve the same result using a single query by combining them with WHERE and OR.
For example:
SELECT * FROM `companies`
WHERE (
`name` = IFNULL(
(SELECT `name` FROM `companies`
WHERE `name` < 'variable_with_current_name'
ORDER BY `name` DESC
LIMIT 1)
, 0)
OR
`name` = IFNULL(
(SELECT `name` FROM `companies`
WHERE `name` > 'variable_with_current_name'
ORDER BY `name` ASC
LIMIT 1)
, 0)
)
Hope that helps.

Related

Is there an alternative to MySQL Field() function in VFP?

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

ORA-00904: invalid column name but I am using the correct column name

Can someone see where I am going wrong in the below query? I am getting the error message that the GROUP BY column doesn't exist, but it clearly does as I see that column name in the output when I don't use the GROUP BY.
SELECT
(SELECT customer_address.post_code FROM customer_address WHERE customer_address.address_type = 0 AND customer_address.customer_no = orders.customer_no) postcode, SUM(orders.order_no) orders
FROM
orders, customer_address
WHERE
orders.delivery_date = '27-MAY-15'
GROUP BY
postcode;
The answer is: You cannot use an alias name in GROUP BY.
So:
GROUP BY (SELECT customer_address.post_code ...);
Or:
select postcode, sum(order_no)
from
(
SELECT
(SELECT customer_address.post_code FROM customer_address WHERE customer_address.address_type = 0 AND customer_address.customer_no = orders.customer_no) postcode,
orders.order_no
FROM orders, customer_address
WHERE orders.delivery_date = '27-MAY-15'
)
GROUP BY postcode;
EDIT:
However, your query seems wrong. Why do you cross-join orders and customer_address? By mistake I guess. Use explicit joins (INNER JOIN customer_address ON ...), when using joins to avoid such errors. But here I guess you'd just have to remove , customer_address.
Then why do you add order numbers? That doesn't seem to make sense.

How to check if a set of values exist in item table in Oracle

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.

Oracle dba_tab_cols query

Hi is it possible to retrieve the primary key and unique key using the dba_tab_cols query?
Is there any query that allows me to retrieve all of the following fields?
Column Name
Data Type
Primary Key
Null/Not Null
Unique Key
Default Value
Extra
Both primary and unique keys can span more than one column, so they wouldn't belong in dba_tab_columns. You'd need to look at dba_constraints and dba_cons_columns to get that information.
This is a starting point, maybe:
select owner, table_name, column_name, data_type, primary_key,
nullable, unique_key, data_default
from (
select dtc.owner, dtc.table_name, dtc.column_id, dtc.column_name,
dtc.data_type, dtc.nullable, dtc.data_default,
case when dc.constraint_type = 'P' and dcc.column_name = dtc.column_name
then dc.constraint_name end as primary_key,
case when dc.constraint_type = 'U' and dcc.column_name = dtc.column_name
then dc.constraint_name end as unique_key,
row_number() over (partition by dtc.owner, dtc.table_name, dtc.column_id
order by null) as rn
from dba_tab_columns dtc
left join dba_constraints dc
on dc.owner = dtc.owner
and dc.table_name = dtc.table_name
and dc.constraint_type in ('P', 'U')
left join dba_cons_columns dcc
on dcc.owner = dc.owner
and dcc.constraint_name = dc.constraint_name
and dcc.table_name = dc.table_name
and dcc.column_name = dtc.column_name
where dtc.owner = '<owner>'
and dtc.table_name = '<table_name>'
)
where rn = 1
order by owner, table_name, column_id;
I've done this with a subquery that generates a row_number value because you'd get duplicates for a table with more than one constraint; and because you want the default value, which is a long (column data_default), you can't use distinct or group by. It feels a bit inelegant, but I'm sure you can work on it to get what you need.
It's also possible to have a check constraint that replicates the not null version, though it isn't advisable. And a unique index won't show up as a unique constraint, so you might want to look for one of those too, via dba_indexes and dba_ind_columns. An index used to back up a unique constrain will appear in both, though.
You could look at dbms_metadata.get_ddl to get this information too, depending on what you intend to do with it. I'm not sure why this would be useful, other than to try to recreate the schema elsewhere, and there are better tools for doing that.

Need to select column from subquery into main query

I have a query like below - table names etc. changed for keeping the actual data private
SELECT inv.*,TRUNC(sysdate)
FROM Invoice inv
WHERE (inv.carrier,inv.pro,inv.ndate) IN
(
SELECT carrier,pro,n_dt FROM Order where TRUNC(Order.cr_dt) = TRUNC(sysdate)
)
I am selecting records from Invoice based on Order. i.e. all records from Invoice which are common with order records for today, based on those 3 columns...
Now I want to select Order_Num from Order in my select query as well.. so that I can use the whole thing to insert it into totally seperate table, let's say orderedInvoices.
insert into orderedInvoices(seq_no,..same columns as Inv...,Cr_dt)
(
SELECT **Order.Order_Num**, inv.*,TRUNC(sysdate)
FROM Invoice inv
WHERE (inv.carrier,inv.pro,inv.ndate) IN
(
SELECT carrier,pro,n_dt FROM Order where TRUNC(Order.cr_dt) = TRUNC(sysdate)
)
)
?? - how to do I select that Order_Num in main query for each records of that sub query?
p.s. I understand that trunc(cr_dt) will not use index on cr_dt (if a index is there..) but I couldn't select records unless I omit the time part of it..:(
If the table ORDER1 is unique on CARRIER, PRO and N_DT you can use a JOIN instead of IN to restrict your records, it'll also enable you to select whatever data you want from either table:
select order.order_num, inv.*, trunc(sysdate)
from Invoice inv
join order ord
on inv.carrier = ord.carrier
and inv.pro = ord.pro
and inv.ndate = ord.n_dt
where trunc(order.cr_dt) = trunc(sysdate)
If it's not unique then you have to use DISTINCT to deduplicate your record set.
Though using TRUNC() on CR_DT will not use an index on that column you can use a functional index on this if you do need an index.
create index i_order_trunc_cr_dt on order (trunc(cr_dt));
1. This is a really bad name for a table as it's a keyword, consider using ORDERS instead.

Resources