SQL- get data from two tables in different columns without using unions - sql

I have a table STOCK that looks like this:
PRODUCT SALES_CODE STOCK_1 STOCK_2 STOCK_3
-----------------------------------------------------
A 6-10 0 1 2
There are many STOCK_X buckets but for simplicity's sake, I've excluded.
Now I have another table SIZE_GRID:
SALES_CODE SIZE_1 SIZE_2 SIZE_3
--------------------------------------
6-10 6 8 10
As you might have guessed, these are stock on hand for a certain product, by size.
I need to get the STOCK values from the first table, and the size from the second table.
Originally, I was doing the following
SELECT
STOCK.PRODUCT,
SIZE_GRID.SIZE_1,
STOCK.STOCK_1
FROM
STOCK
INNER JOIN
SIZE_GRID ON
SIZE_GRID.SALES_CODE = STOCK.SALES_CODE
UNION ALL
SELECT
STOCK.PRODUCT,
SIZE_GRID.SIZE_2,
STOCK.STOCK_2
FROM
STOCK
INNER JOIN
SIZE_GRID ON
SIZE_GRID.SALES_CODE = STOCK.SALES_CODE
UNION ALL
SELECT
STOCK.PRODUCT,
SIZE_GRID.SIZE_3,
STOCK.STOCK_3
FROM
STOCK
INNER JOIN
SIZE_GRID ON
SIZE_GRID.SALES_CODE = STOCK.SALES_CODE
I have around 40 STOCK_X that I need to retrieve, so wandering if there is a much easier way to do this? Preferably I want to use pure SQL and no UDF/SP's.
http://sqlfiddle.com/#!6/f323e

If you are on SQL Server 2008 or later version, you could try the following method (found here):
SELECT
STOCK.PRODUCT,
X.SIZE,
X.STOCK
FROM
STOCK
INNER JOIN
SIZE_GRID ON
SIZE_GRID.SALES_CODE = STOCK.SALES_CODE
CROSS APPLY (
VALUES
(SIZE_GRID.SIZE_1, STOCK.STOCK_1),
(SIZE_GRID.SIZE_2, STOCK.STOCK_2),
(SIZE_GRID.SIZE_3, STOCK.STOCK_3)
) X (SIZE, STOCK)
;
With a small tweak you could make it work in SQL Server 2005 as well:
SELECT
STOCK.PRODUCT,
X.SIZE,
X.STOCK
FROM
STOCK
INNER JOIN
SIZE_GRID ON
SIZE_GRID.SALES_CODE = STOCK.SALES_CODE
CROSS APPLY (
SELECT SIZE_GRID.SIZE_1, STOCK.STOCK_1
UNION ALL
SELECT SIZE_GRID.SIZE_2, STOCK.STOCK_2
UNION ALL
SELECT SIZE_GRID.SIZE_3, STOCK.STOCK_3
) X (SIZE, STOCK)
;
However, if you are using an even earlier version, this might be of help:
SELECT
STOCK.PRODUCT,
SIZE = CASE X.N
WHEN 1 THEN SIZE_GRID.SIZE_1
WHEN 2 THEN SIZE_GRID.SIZE_2
WHEN 3 THEN SIZE_GRID.SIZE_3
END,
STOCK = CASE X.N
WHEN 1 THEN STOCK.STOCK_1
WHEN 2 THEN STOCK.STOCK_2
WHEN 3 THEN STOCK.STOCK_3
END,
FROM
STOCK
INNER JOIN
SIZE_GRID ON
SIZE_GRID.SALES_CODE = STOCK.SALES_CODE
CROSS JOIN (
SELECT 1
UNION ALL
SELECT 2
UNION ALL
SELECT 3
) X (N)
;
Although the last two options use UNION ALL, they are combining single rows only, not entire subsets

Consider normalizing the table. Instead of a repeating column:
PRODUCT SALES_CODE STOCK_1 STOCK_2 STOCK_3
Use a normalized table:
PRODUCT SALES_CODE STOCK_NO STOCK
And the same for the SIZE_GRID table:
SALES_CODE SIZE_NO SIZE
Now you can query without the need to list 40 columns:
select *
from STOCK s
join SIZE_GRID sg
on sg.SALES_CODE = s.SALES_CODE
and sg.SIZE_NO = s.STOCK_NO

Here are some alternatives you can use:
Execute each SQL separately and merge and sort the result sets within your program
Join the tables.
Use a scalar subquery.
select
select col1, col2, col3 from Table_1 q1,
select col1, col2,
col3 from Table_2 q2 from dual;
Try UNION using FULL OUTER JOIN with the NVL function: It is suggested that this has faster performance than the UNION operator.
select
empno,
ename,
nvl(dept.deptno,emp.deptno) deptno, dname from emp
full outer join
dept
on
(emp.deptno = dept.deptno)
order by 1,2,3,4;

Related

How can I perform this sql update using sql instead of using code?

I have been able to select this data, using these two sql queries
Query 1:
SELECT article_id, amount_required, amount_sold FROM products_articles,sales WHERE sales.product_id = products_articles.product_id
Query 2:
SELECT * FROM articles
What I want to do, is go through the first table (with amount sold and required) (it's fine that there are duplicate rows), and for each row in the table multiply the value of amount_sold and amount_required and then subtract that value from amount_in_stock where the ids match in the second table.
Example from the first row:
2 * 4 = 8, change amount_in_stock from 124 to 116.
And so on...
How can I do this using just sql?
UPDATE A
SET
A.amount_in_stock =(S.amountSold * S.amount_required)- A.amount_in_stock
FROM articles AS A
INNER JOIN
products_articles AS PA
ON PA.article_id= A.article_id
INNER JOIN Sales AS S
ON S.product_id=PA.product_id
Please try this:
Update articles a
inner join
(
SELECT article_id, sum(amount_required) amount_required, sum(amount_sold )amount_sold FROM products_articles inner join sales on sales.product_id = products_articles.product_id
group by article_id
)b on a.article_id=b.article_id
set a.amount_in_stock=a.amount_in_stock-(amount_required*amount_sold )
Since there could be multiple rows in product_articles and amount_sold I have used group by to sum the amounts.
For SQLite please try this:
Update articles
set amount_in_stock=(SELECT sum(amount_required) * sum(amount_sold ) FROM products_articles inner join sales on sales.product_id = products_articles.product_id
where products_articles.article_id=articles.article_id
group by article_id
)
where exists (SELECT * FROM products_articles inner join sales on sales.product_id = products_articles.product_id where products_articles.article_id=articles.article_id
)

SQL Table Joining

I'm joining these three tables, but the same information gets displayed 3 times ... Any idea how to have only the unique rows to be displayed, as determined by unique shipment id's?
SELECT S.SHIPMENT_ID, S.CREATION_DATE, S.BUSINESS_ID, B.BUS_ID, S.SHIPMENT_STATUS, S.BUSINESS_NAME, S.SHIPMENT_MODES, S.CUSTOMER_NAME
FROM "SHIPMENT" S
INNER JOIN "BUSINESS" B ON S.BUSINESS_ID=B.BUS_ID
INNER JOIN "SHIPMENT_GROUP" SG ON S.SHIPMENT_ID=SG.SHIPMENT_ID
INNER JOIN "DATA_GROUP" DG ON DG.ID=SG.GROUP_ID
try select distinct
SELECT DISTINCT column1, column2, ...
FROM table_name;
w3schools
You are selecting rows from the first table only, so this suggests that you are using the joins for filtering.
If so, you can rewrite this with exists, which will avoid duplicates if there are multiple matches. Starting from your existing query, the logic would be:
select s.*
from shipment s
where
exists (
select 1
from business b
where b.bus_id = s.business_id
) and exists (
select 1
from shipment_group sg
inner join data_group dg on dg.id = sg.group_id
where sg.shipment_id = s.shipment_id
)

Join and compare 2 queries of 2 tables

This is probably a quite trivial question for many here but I am not used to write sub queries and joins, so I hope someone want to help.
I have two tables: new_road and old_roads.
These two queries sum up the length of the roads belonging to a specific road number.
SELECT new_road.nummer, SUM(new_road.length) FROM road_table.road GROUP BY new_road.nummer
SELECT old_road.nummer, SUM(ST_length(old_road.geom)) FROM old_road_table.old_road GROUP BY old_road.nummer
I wish to have a result table where these two queries are joined so I can compare the new and old summed length for each road number.
Like
old.nummer old.length new.nummer new.lenght
2345 10.3 2345 10.5
2346 578.2 2346 600
2347 54.2 NULL NULL
NULL NULL 2546 32.2
I think some version of an outer join is needed because there will be a road numbers in the old_road table that does not exist in the new.road table and i would like to see them too.
Appreciate any advice
Edit:
After advice from below did I came up with this:
SELECT * FROM
(SELECT new_road.nummer, SUM(new_road.length) FROM road_table.road GROUP BY new_road.nummer) new_table
FULL OUTER JOIN
(SELECT old_road.nummer, SUM(ST_length(old_road.geom)) FROM old_road_table.old_road GROUP BY old_road.nummer) old_table
ON new_road.nummer = old_road.nummer
But each time I run it I get missing FROM-clause entry. When I run each sub query individually they work. I have crosschecked with the documentation and it look OK to me, but clearly I am missing something here.
Consider using a FULL OUTER JOIN
This is not the exact output you requested but you don't need to display the nummer twice.
SELECT
COALESCE(new_road.nummer,old_road.nummer)nummer,
new_road.length,
old_road.length
FROM (
SELECT new_road.nummer
,SUM(new_road.length) length
FROM road_table.road
GROUP BY new_road.nummer
) new_road
FULL OUTER JOIN (
SELECT old_road.nummer
,SUM(ST_length(old_road.geom))length
FROM old_road_table.old_road
GROUP BY old_road.nummer
) old_road ON
old_road.nummer = new_road.nummer
Following query should solve the purpose. I didn't run it but the basic idea is result of a query on a table is another table on which you can query again.
Select * FROM (SELECT new_road.nummer, SUM(new_road.length) FROM road_table.road GROUP BY new_road.nummer) table1 JOIN (SELECT old_road.nummer, SUM(ST_length(old_road.geom)) FROM old_road_table.old_road GROUP BY old_road.nummer) table2 ON table1.new_road.nummer = table2.old_road.nummer
The tricky bit here is that you want to make sure you include all of the keys from both lists. My favorite way to do this kind of thing is:
select * from (
SELECT distinct new_road.nummer as nummer from road_table.road
union
SELECT distinct old_road.nummer as nummer FROM old_road_table.old_road
) allkeys
left join
(
SELECT new_road.nummer as nummer, SUM(new_road.length) as nlen
FROM road_table.road GROUP BY new_road.nummer
) n
on allkeys.nummer = n.nummer
left join
(
SELECT old_road.nummer as nummer, SUM(ST_length(old_road.geom)) as olen
FROM old_road_table.old_road GROUP BY old_road.nummer
) o
on allkeys.nummer = o.nummer
The first subquery builds a list of all keys, then you join to both of your queries from there. There's nothing wrong with an outer join, but I find this easier to manage if you have to include 3 or more tables. If you had to include another table it would just be one more union in allkeys and one more left join to that table.

MS SQL Combine 2 tables rows(for each row select all table rows)

I have 2 tables:
1 - prices:
PID, Price
2 - prices multipliers:
MID, PriceMultiplier
I need to combine these tables using next approach:
For each multiplier select PID, MID, Price*PriceMultiplier
As I understood using simple query it's not possible, but I can't find way how to do this using stored procedure
(from a comment) The way is to select each row for each row
You can use CROSS JOIN for that:
SELECT p.PID, m.MID, p.Price * m.PriceMultiplier
FROM Price p
CROSS JOIN Multiplier m
This will produce a {price, multiplier} pair for each combination of rows from both tables.

Oracle Query Join issue

I have an Oracle table as shown below
Orders
---------
ORDERID
DESCRIPTION
TOTALVALUE
ORDERSTATUS
I have the below mentioned query
select ORDERID,ORDERSTATUS
FROM ORDERS
WHERE ORDERID IN( 1000,1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1020,
1030,104,1040,1090,789)
Some orderIDs mentioned above are not in orders table. In spite of that I want the orderIDs to appear in the resultset with status as null.
Appreciate your help.
What about this:
SELECT T.COLUMN_VALUE AS ORDERID, ORD.ORDERSTATUS
FROM TABLE(SYS.ODCINUMBERLIST(
1000,1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1020,1030,104,1040,1090,789
)) T
LEFT JOIN ORDERS ORD ON ORD.ORDERID = T.COLUMN_VALUE;
You can also get it to work if the order IDs aren't fixed but a parameter. But the solution depends on whether you call the statement from PL/SQL or from another programming language such as C#, Java, PHP etc.
Update:
SYS.ODCINUMBERLIST is just a table type that's already defined in Oracle. You could use your own type:
CREATE TYPE NUMBER_TABLE_T AS TABLE OF NUMBER;
You can use a CTE as table for the orderIds (or store them into a temporary table), and outer join your Orders:
With tmp As (
Select 1000 As orderId From dual
Union All
Select 1001 From dual
Union All
...
)
Select tmp.orderId, o.orderStatus
From tmp
Left Join orders o On ( o.orderId = tmp.orderId )
orderStatus is NULL, when no order is found.
You would have to do an outer join to accomplish something like this :
SELECT ORDERID, ORDERSTATUS
FROM (
SELECT 1000 AS ORDERID FROM dual UNION SELECT 1001 FROM dual -- etc
) tmpOrderid
LEFT OUTER JOIN ORDERS O
ON tmpOrderid.ORDERID = O.ORDERID;
I have never used Oracle, but there is most likely a function that can generate numbers (for exemple, generate_series(1000, 1010) in PostgreSQL).
there is one more trick in oracle.
SELECT LEVEL + 1000 dt FROM DUAL CONNECT BY LEVEL < (2000 - 1000)
it generates a recordset with 1000 rows which might be left joined with your table.