I asked the stuff function in Oralce question several months ago and would like to revisit this question. The problem was when using the WM_CONCAT function (in Oracle), the result included the (,) in the front or before the field I wanted to stuff.
I googled to find a solution for this but had no success so far. What should I need to include in the WM_CONCAT function in order to remove the extra empty string for the field I'd like to stuff?
I have some sample records below
CREATE TABLE #TEMP
(
COMPANY VARCHAR (50),
ID INT,
PRODUCT VARCHAR(50)
)
INSERT INTO #TEMP VALUES ('APPLES', '123', 'IPHONE4')
INSERT INTO #TEMP VALUES ('APPLES', '123', 'IPHONE4S')
INSERT INTO #TEMP VALUES ('APPLES', '123', 'IPHONE5')
INSERT INTO #TEMP VALUES ('SAMSUNG', '124', 'GALAXY S2')
INSERT INTO #TEMP VALUES ('SAMSUNG', '124', 'GALAXY S3')
INSERT INTO #TEMP VALUES ('SAMSUNG', '124', 'GALAXY S4')
INSERT INTO #TEMP VALUES ('NOKIA', '125', 'C5-02')
INSERT INTO #TEMP VALUES ('NOKIA', '125', 'C5-03')
INSERT INTO #TEMP VALUES ('NOKIA', '125', 'C5-04')
When running this query it produces as
SELECT
COMPANY,
ID,
WM_CONCAT(PRODUCT) AS PRODUCT, --STUFF PRODUCT COLUMN
FROM
(
SELECT
COMPANY,
ID,
PRODUCT
FROM #TEMP
)
GROUP BY COMPANY, ID
COMPANY ID PRODUCT
APPLES 123 IPHONE4, IPHONE4S, IPHONE5
SAMSUNG 124 ,GALAXY S2, GALAXY S3, GALAXY S4
NOKIA 125 ,C5-02, C5-03, C5-04
Just replace the:
WM_CONCAT(PRODUCT)
with:
LTRIM(WM_CONCAT(PRODUCT),',')
This will remove any leading commas from your column.
Try with the following query
SELECT
COMPANY,
ID,
WM_CONCAT(PRODUCT) AS PRODUCT
FROM
(
SELECT
COMPANY,
ID,
PRODUCT
FROM TEMP
)
GROUP BY COMPANY, ID
Is this the output you are looking for?
NOKIA 125 C5-02,C5-04,C5-03
APPLES 123 IPHONE4,IPHONE5,IPHONE4S
SAMSUNG 124 GALAXY S2,GALAXY S4,GALAXY S3
Query can be this way as well
SELECT
COMPANY,
ID,
WM_CONCAT(PRODUCT) AS PRODUCT
FROM TEMP
--(
-- SELECT
-- COMPANY,
-- ID,
-- PRODUCT
-- FROM TEMP
-- )
GROUP BY COMPANY, ID
In Oracle PL/SQL, at least in the version 19c that I'm using, there is a LISTAGG() function that is the equivalent of Stuff(). There is no WM_CONCAT in that implementation.
Related
Need help query performance.
I have a table A joining to a view and it is taking 7 seconds to get the results. But when i do select query on view i get the results in 1 seconds.
I have created the indexes on the table A. But there is no improvements in the query.
SELECT
ITEM_ID, BARCODE, CONTENT_TYPE_CODE, DEPARTMENT, DESCRIPTION, ITEM_NUMBER, FROM_DATE,
TO_DATE, CONTACT_NAME, FILE_LOCATION, FILE_LOCATION_UPPER, SOURCE_LOCATION,
DESTRUCTION_DATE, SOURCE, LABEL_NAME, ARTIST_NAME, TITLE, SELECTION_NUM, REP_IDENTIFIER,
CHECKED_OUT
FROM View B,
table A
where B.item_id=A.itemid
and status='VALID'
AND session_id IN ('naveen13122016095800')
ORDER BY item_id,barcode;
CREATE TABLE A
(
ITEMID NUMBER,
USER_NAME VARCHAR2(25 BYTE),
CREATE_DATE DATE,
SESSION_ID VARCHAR2(240 BYTE),
STATUS VARCHAR2(20 BYTE)
)
CREATE UNIQUE INDEX A_IDX1 ON A(ITEMID);
CREATE INDEX A_IDX2 ON A(SESSION_ID);
CREATE INDEX A_IDX3 ON A(STATUS);'
So querying the view joined to a table is slower than querying the view alone? This is not surprising, is it?
Anyway, it doesn't make much sense to create separate indexes on the fields. The DBMS will pick one index (if any) to access the table. You can try a composed index:
CREATE UNIQUE INDEX A_IDX4 ON A(status, session_id, itemid);
But the DBMS will still only use this index when it sees an advantage in this over simply reading the full table. That means, if the DBMS expects to have to read a big amount of records anyway, it won't indirectly access them via the index.
At last two remarks concerning your query:
Don't use those out-dated comma-separated joins. They are less readable and more prone to errors than explicit ANSI joins (FROM View B JOIN table A ON B.item_id = A.itemid).
Use qualifiers for all columns when working with more than one table or view in your query (and A.status='VALID' ...).
UPDATE: I see now, that you are not selecting any columns from the table, so why join it at all? It seems you are merely looking up whether a record exists in the table, so use EXISTS or IN accordingly. (This may not make it faster, but a lot more readable at least.)
SELECT
ITEM_ID, BARCODE, CONTENT_TYPE_CODE, DEPARTMENT, DESCRIPTION, ITEM_NUMBER, FROM_DATE,
TO_DATE, CONTACT_NAME, FILE_LOCATION, FILE_LOCATION_UPPER, SOURCE_LOCATION,
DESTRUCTION_DATE, SOURCE, LABEL_NAME, ARTIST_NAME, TITLE, SELECTION_NUM, REP_IDENTIFIER,
CHECKED_OUT
FROM View
WHERE itemid IN
(
SELECT itemid
FROM A
WHERE status = 'VALID'
AND session_id IN ('naveen13122016095800')
)
ORDER BY item_id, barcode;
I'm trying to insert records for couple of columns from a physical table into a temp table with customized IDENTITY. It creates the identity column (field name = idnum), but the values are 0 for all rows. I'm using below code. If anyone can help me what I'm doing wrong would be greatly appreciated.
Note: I'm trying this is Sybase ASE 15.7
SELECT
* INTO #achu_test
FROM (SELECT TOP 10
idnum = IDENTITY(8),
First_Name,
Last_Name
FROM Employees) myTable
My bad! I misplaced the IDENTITY. instead of using it before "* INTO", I used inside the Subquery.
SELECT idnum = IDENTITY(8),* INTO #achu_test
FROM (SELECT TOP 10 First_Name, Last_Name FROM Employees) myTable
A good sleep might have given the result for me :)
I've two table in the database, the first one is Person and the second is Pilot. as following:
Person Table:
CREATE TABLE person(
person_id NUMBER PRIMARY KEY,
last_name VARCHAR2(30) NOT NULL,
first_name VARCHAR2(30) NOT NULL,
hire_date VARCHAR2(30) NOT NULL,
job_type CHAR NOT NULL,
job_status CHAR NOT NULL
);
/
INSERT INTO person VALUES (1000, 'Smith', 'Ryan', '04-MAY-90','F', 'I');
INSERT INTO person VALUES (1170, 'Brown', 'Dean', '01-DEC-92','P', 'A');
INSERT INTO person VALUES (2010, 'Fisher', 'Jane', '12-FEB-95','F', 'I');
INSERT INTO person VALUES (2080, 'Brewster', 'Andre', '28-JUL-98', 'F', 'A');
INSERT INTO person VALUES (3190, 'Clark', 'Dan', '04-APR-01','P', 'A');
INSERT INTO person VALUES (3500, 'Jackson', 'Tyler', '01-NOV-05', 'F', 'A');
INSERT INTO person VALUES (4000, 'Miller', 'Mary', '11-JAN-08', 'F', 'A');
INSERT INTO person VALUES (4100, 'Jackson', 'Peter', '08-AUG-11', 'P','I');
INSERT INTO person VALUES (4200, 'Smith', 'Ryan', '08-DEC-12', 'F','A');
COMMIT;
/
Pilot Table:
CREATE TABLE pilot(
person_id NUMBER PRIMARY KEY,
pilot_type VARCHAR2(100) NOT NULL,
CONSTRAINT fk_person_pilot FOREIGN KEY (person_id)
REFERENCES person(person_id)
);
/
INSERT INTO pilot VALUES (1170, 'Commercial pilot');
INSERT INTO pilot VALUES (2010, 'Airline transport pilot');
INSERT INTO pilot VALUES (3500, 'Airline transport pilot');
COMMIT;
/
I'm asked to write a pl/sql block of code that accepts the last name from the user and return the result as following:
1) if the last name is not in the table, it returns all the rows in the table.
2) if the last name is in the table, it shows all of the employee's information.
So far I'm doing well with the code, but I got stuck in the case that there are two employees with the last name. here is the cursor that I wrote:
cursor person_info is
select last_name, first_name, hire_date, job_type, job_status, nvl(pilot_type, '-----------')
from person
full outer join pilot
on person.person_id = pilot.person_id
where upper(last_name) = upper(v_last_name)
group by last_name, first_name, hire_date, job_type, job_status, pilot_type
order by last_name, first_name, hire_date asc;
Logically, there are three cases to be covered:
the first case, when the entered last name is in the table, I return all the rows in the table and that's done.
The second case when there is only one employee with the entered last name, and this case is done as well. The last case when there are more than one employee having the same last name like for example 'Jackson' or 'Smith' in this case, my program crashes and give me the error that my select into statement returns more than one row.
select person_id
into v_n
from person
where upper(last_name) = upper(v_last_name);
if v_n = 1 then
open person_info;
fetch person_info into v_last_name, v_first_name, v_hire_date, v_job_type, v_job_status, v_pilot_type;
Can someone help me in guiding me how to fetch the data correctly? I'm not allowed to create any temporary tables or views.
I'm so sorry for making the problem longer than it should, but I was trying to be as clear as possible in explaining the problem.
Thank you in advance.
if the error is
"ORA-01422 exact fetch returns more than requested number of rows" then I think your answer is here https://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:981494932508
If you EXPECT the query to return more than one row, you would code:
for x in ( select * from t where ... )
loop
-- process the X record here
end loop;
Your immediate issue is that you're selecting the matching person_id into a variable, and then seeing if that specific ID is 1. You don't have an actual ID 1 anyway so that check would never match; but it is that querying matching multiple rows that gets the error, as you can't put two matching IDs into a single scalar variable.
The way you've structured it looks like you are trying to count how many matching rows there are, rather than looking for a specific ID:
select count(person_id)
into v_n
from person
where upper(last_name) = upper(v_last_name);
if v_n = 1 then
....
When you do have multiple matches then you will need to use the same mechanism to return all of those as you do when there are no matches and you return all employees. You may find the logic should be in the cursor query rather then in PL/SQL logic. It depends on the details of the assignment though, and how it expects you to return the data in both (or all three) scenarios.
It's also possible you just aren't expected to hit this problem - it isn't clear if the assignment is finding all employees, or only those that are pilots. The issue still exists in general, but with the data you show there aren't any duplicate pilot last names. If you haven't learned about this kind of error yet perhaps you're getting a bit ahead of what your tutor expects.
I am using Oracle Database.
I have view called VW_MREQ
It has column as follows :
M_Product_ID
AD_Client_ID
AD_ORG_ID
It has records inside.
Then, I have empty table called M_Requisition
It has column as follows :
M_Product_ID
AD_Client_ID
AD_ORG_ID
DESCRIPTION
CREATEDBY
I am making Procedure and would like to insert Data manually to M_Requisition, the foreign key is M_Product_ID and I want the AD_Client_ID and AD_ORG_ID to be the same as in VM_REQ as I insert M_Product_ID to M_Requisition manually.
INSERT INTO M_Requisition(M_Product_ID, AD_Client_ID, AD_ORG_ID, DESCRIPTION, CREATEDBY) VALUES(123, ?? , ??,"Insert Data","Me")
I plan to use SELECT INTO but still confused how I arrange it as I am newbie in Oracle.
Your help will be useful.
You could use the insert-select syntax, and just query the hardcoded values as literals:
INSERT INTO M_Requisition
(M_Product_ID, AD_Client_ID, AD_ORG_ID, DESCRIPTION, CREATEDBY)
(SELECT 123, AD_Client_ID, AD_ORG_ID, 'Insert Data', 'Me'
FROM VW_MREQ)
In PL SQL, I'm writing a stored procedure that uses a DB link:
CREATE OR REPLACE PROCEDURE Order_Migration(us_id IN NUMBER, date_id in DATE)
as
begin
INSERT INTO ORDERS(order_id, company_id)
SELECT ORDER_ID_SEQ.nextval, COMPANY_ID
FROM ORDERS#SOURCE
WHERE USER_ID = us_id AND DUE_DATE = date_ID;
end;
It takes all orders done on a certain day, by a certain user and inserts them in the new database. It calls a sequence to makes sure there are no repeat PKs on the orders, and it works well.
However, I want the same procedure to do a second INSERT into another table that has order_id as a foreign key. So I need to add all the order_id's just created, and the data from SOURCE that matches:
INSERT INTO ORDER_COMPLETION(order_id, completion_dt)
SELECT ????, completion_dt
FROM ORDER_COMPLETION#SOURCE
How can I keep track of which order_id that was just created matches up to the one whose data I need to pull from the source database?
I looked into making a temporary table, but you can't create those in a procedure.
Other info: I'll be calling this procedure from a C# app I'm writing
I'm not sure that I follow the question. If there is an ORDERS table and an ORDER_COMPLETION table in the remote database, wouldn't there be some key on the source system that related those two tables? If that key is the ORDER_ID, why would you want to re-assign that key in your procedure? Wouldn't you want to maintain the ORDER_ID from the source system?
If you do want to re-assign the ORDER_ID locally, I would tend to think that you'd want to do something like
CREATE OR REPLACE PROCEDURE order_migration( p_user_id IN orders.user_id%type,
p_due_date IN orders.due_date%type )
AS
TYPE order_rec IS RECORD( new_order_id NUMBER,
old_order_id NUMBER,
company_id NUMBER,
completion_dt DATE );
TYPE order_arr IS TABLE OF order_rec;
l_orders order_arr;
BEGIN
SELECT order_id_seq.nextval,
o.order_id,
o.company_id,
oc.completion_dt
BULK COLLECT INTO l_orders
FROM orders#source o,
order_completion#source oc
WHERE o.order_id = oc.order_id
AND o.user_id = p_user_id
AND o.due_date = p_due_date;
FORALL i IN l_orders.FIRST .. l_orders.LAST
INSERT INTO orders( order_id, company_id )
VALUES( l_orders(i).new_order_id, l_orders(i).company_id );
FORALL i IN l_orders.FIRST .. l_orders.LAST
INSERT INTO order_completion( order_id, completion_dt )
VALUES( l_orders(i).new_order_id, l_orders(i).completion_dt );
END;
You could also do a single FOR loop with two INSERT statements rather than two FORALL loops. And if you're pulling a lot of data each time, you probably want to pull the data in chunks from the remote system by adding a loop and a LIMIT to the BULK COLLECT
There must be some link between the rows in ORDERS#SOURCE and ORDERS, and between ORDERS#SOURCE and ORDER_COMPLETION#SOURCE, so can you not use a join?
Something like:
INSERT INTO ORDER_COMPLETION(order_id, completion_dt)
SELECT o.order_id, ocs.completion_dt
FROM ORDER_COMPLETION#SOURCE ocs
JOIN ORDERS o ON o.xxx = ocs.xxx