SQL join rows in two tables - sql

I'm not good at SQL and wonder if this can be done: I have two tables: table_a and table_b. Both tables have a TEXT type column named category.
Example:
Table_a
|-id-|-category-|
| 1 | fruits |
| 2 | meats |
| 3 | fruits |
| 4 | sweets |
| 5 | meats |
Table_b
|-id-|-category-|
| 1 | veggies |
| 2 | meats |
| 3 | veggies |
| 4 | veggies |
| 5 | meats |
What I need is to select all distinct categories from both tables in alphabetic order.
The result should be:
fruits
meats
sweets
veggies
Thank you

You should use UNION and an ORDER BY clause :
SELECT DISTINCT category
FROM Table_A
UNION
SELECT DISTINCT category
FROM Table_B
ORDER BY category

In sql you could use union and order by
select distinct category from (
select category
from table_a
order by category
union
select category
from table_b )

Related

SQL generate Data based of the ids of three tables

I have three tables store, gender, age_group each of these tables have ids. I need to generate table data for each one all possible combinations of the three.
ex. store_id = (1,2,3) gender_id = (1,2,3) age_group_id = (1,2,3)
so that i have a table that looks like this:
|store_id|gender_id|age_group_id|
|:------:|:-------:|:----------:|
| 1 | 1 | 1 |
| 1 | 2 | 1 |
| 1 | 3 | 1 |
| 2 | 1 | 3 |
| 2 | 2 | 3 |
| 3 | 1 | 3 |
| 3 | 2 | 3 |
etc. continuing on until each combination is populated, any suggestions on best approach to do this in SQL
Cross join the three tables:
select
s.Id as store_id,
g.Id as gender_id,
a.Id as age_group_id
from store s
cross join gender g
cross join age_group a

SQL group by a field and only return one joined row for each grouping

Table data
+-----+----------------+--------+----------------+
| ID | Required_by | Name | Another_Field |
+-----+----------------+--------+----------------+
| 1 | 7 August | cat | X |
| 2 | 7 August | cat | Y |
| 3 | 10 August | cat | Z |
| 4 | 11 August | dog | A |
+-----+----------------+--------+----------------+
What I want to do is group by the name, then for each group choose one of the rows with the earliest required by date.
For this data set, I would like to end up with either rows 1 and 4, or rows 2 and 4.
Expected result:
+-----+----------------+--------+----------------+
| ID | Required_by | Name | Another_Field |
+-----+----------------+--------+----------------+
| 1 | 7 August | cat | X |
| 4 | 11 August | dog | A |
+-----+----------------+--------+----------------+
OR
+-----+----------------+--------+----------------+
| ID | Required_by | Name | Another_Field |
+-----+----------------+--------+----------------+
| 2 | 7 August | cat | Y |
| 4 | 11 August | dog | A |
+-----+----------------+--------+----------------+
I have something that returns 1,2 and 4 but I'm not sure how to only pick one from the first group to get the desired result. I'm joining the grouping with the data table so that I can get the ID and another_field back after the grouping.
SELECT d.id, d.name, d.required_by, d.another_field
FROM
(
SELECT min(required_by) as min_date, name
FROM data
GROUP BY name
) agg
INNER JOIN
data d
on d.required_by = agg.min_date AND d.name = agg.name
This is typically solved using window functions:
select d.id, d.name, d.required_by, d.another_field
from (
select id, name, required_by, another_field,
row_number() over (partition by name order by required_by) as rn
from data
) d
where d.rn = 1;
In Postgres using distinct on() is typically faster:
select distinct on (name) *
from data
order by name, required_by
Online example
SELECT [id]
,[date]
,[name]
FROM [test].[dbo].[data]
WHERE date IN (SELECT min(date) FROM data GROUP BY name)
enter image description here

Oracle - Fill null values in a column with values from another column

I am using Oracle 11.1.1.9.0 and my goal is to fill the Null values with the first NOT NULL values in "Raw Materials" column by each product i.e A, B and C in Product column. An example table and the intended result are illustrated at the end of this request.
None of the code sets in below works:

CODE 1:
IFNULL(Raw Materials,
First_value(Raw Materials) OVER (PARTITION BY Product))

CODE 2:
IFNULL(Raw Materials, 
First_value(Raw Materials) OVER (PARTITION BY Product
RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW))

CODE 3:
COALESCE(lag(Raw Materials ignore null) OVER (partition by Product),
Raw Materials)
CODE 4:
IFNULL(Raw Materials, EVALUATE('LAG(%1, 1) OVER (PARTITION BY %2)' AS varchar2(20), Raw Materials, Product))
Note: IFNULL function does work in the environment. It was tested with IFNULL(Raw Materials, '1') and it resulted in all null values becoming 1 in Raw Materials column.
Thank you.
+---------+----------+ +---------+----------+
| product | material | | product | material |
+---------+----------+ +---------+----------+
| A | | | A | Apple |
| A | | | A | Apple |
| A | | | A | Apple |
| A | | | A | Apple |
| A | Apple | | A | Apple |
| B | | | B | Orange |
| B | | | B | Orange |
| B | | => | B | Orange |
| B | | | B | Orange |
| B | Orange | | B | Orange |
| C | | | C | Banana |
| C | | | C | Banana |
| C | | | C | Banana |
| C | | | C | Banana |
| C | Banana | | C | Banana |
+---------+----------+ +---------+----------+
Left is the example table data. Right is the intended result.
The below link "Oracle code environment" shows the code environment and samples of Oracle Logical SQL function.
Oracle code environment
Oracle Logical SQL manual: https://docs.oracle.com/middleware/11119/biee/BIEUG/appsql.htm#CHDDCFJI
For your dataset, you could simply do a window MAX() or MIN():
NVL(Raw_Materials, MAX(Raw_Materials) OVER(PARTITION BY Product))
If you have a column that can be used to order the rows (I assumed id), you can use LAG() with the IGNORE NULLS clause:
NVL(Raw_Materials, LAG(Raw_Materials IGNORE NULLS) OVER(PARTITION BY Product ORDER BY id))
While you say that you are looking for some "first" value, your sample data suggests that you just want all same products to have the same material:
update mytable m1 set material =
(
select min(material)
from mytable m2
where m2.product = m1.product
);
If you just want to select this data. Then you can use this:
select product, min(material) over (partition by product)
from mytable;
According to the docs (https://docs.oracle.com/cd/E28280_01/bi.1111/e10540/sqlref.htm#BIEMG678) it seems OBIEE uses a special syntax for analytic window functions (e.g. MIN() OVER()):
select
product,
evaluate('min(%1) over (partition by %2)', material, product)
from mytable;
You must enable this by seeting the EVALUATE_SUPPORT_LEVEL accordingly.
(I hope I got this right. Otherwise read the docs on this and try something along the lines for yourself.)
You can try below query,We are using First value analytic function nullif, COALESCE, etc work on row level not column level.
with temp as (select 'A' product,NULL raw_material from dual union all
select 'A',NULL from dual union all
select 'A',NULL from dual union all
select 'A',NULL from dual union all
select 'A','APPLE' from dual union all
select 'B',NULL from dual union all
select 'B',NULL from dual union all
select 'B',NULL from dual union all
select 'B',NULL from dual union all
select 'B','ORANGE' from dual union all
select 'C',NULL from dual union all
select 'C',NULL from dual union all
select 'C',NULL from dual union all
select 'C',NULL from dual union all
select 'C',NULL from dual union all
select 'C','Banana' from dual)
select a.*,FIRST_VALUE(raw_material IGNORE NULLS)
OVER (partition by product ORDER BY product) first_product from temp a;
Oracle does not have an IFNULL function. Your code would have worked if you swapped IFNULL for COALESCE in either of your first two code snippets:
SELECT t.*,
COALESCE(
raw_material,
FIRST_VALUE(raw_material)
IGNORE NULLS
OVER ( PARTITION BY product )
) AS updated_raw_material
FROM test_data t;
Outputs:
PRODUCT | RAW_MATERIAL | UPDATED_RAW_MATERIAL
:------ | :----------- | :-------------------
A | null | Apple
A | null | Apple
A | null | Apple
A | Apple | Apple
B | null | Orange
B | null | Orange
B | null | Orange
B | null | Orange
B | Orange | Orange
C | null | Banana
C | null | Banana
C | null | Banana
C | null | Banana
C | null | Banana
C | Banana | Banana
db<>fiddle here

BigQuery Standard SQL Group by aggregate multiple columns

Sample dataset:
|ownerId|category|aggCategory1|aggCategory2|
--------------------------------------------
| 1 | dog | animal | dogs |
| 1 | puppy | animal | dogs |
| 2 | daisy | flower | ignore |
| 3 | rose | flower | ignore |
| 4 | cat | animal | cats |
...
Looking to do a group by that contains number of owners from category, aggCategory1, aggCategory2 for example outputting:
|# of owners|summaryCategory|
-----------------------------
| 1 | dog |
| 1 | puppy |
| 1 | daisy |
| 1 | rose |
| 1 | cat |
| 2 | animal |
| 2 | flower |
| 1 | dogs |
| 2 | ignore |
| 1 | cats |
Doesn't have to be that format but looking to get the above data points.
Thanks!
One method is to use union all to unpivot the data and then aggregation in an outer query:
SELECT category, COUNT(*)
FROM (SELECT ownerID, category
FROM t
UNION ALL
SELECT ownerID, aggCategory1
FROM t
UNION ALL
SELECT ownerID, aggCategory2
FROM t
) t
GROUP BY category
The more BigQuery'ish way to write this uses arrays:
SELECT cat, COUNT(*)
FROM t CROSS JOIN
UNNEST(ARRAY[category, aggcategory1, aggcategory2]) cat
GROUP BY cat;
SELECT COUNT(T.ownerID), T.category
FROM (
SELECT ownerID, category
FROM table
UNION
SELECT ownerID, aggCategory1
FROM table
UNION
SELECT ownerID, aggCategory2
FROM table
) AS T
GROUP BY T.category
With a GROUP BY and the union with all of yours categories columns, it can be good.
use union all
with cte as
(
SELECT ownerID, category as summaryCategory
FROM table
UNION
SELECT ownerID, aggCategory1 as summaryCategory
FROM table
UNION
SELECT ownerID, aggCategory2 as summaryCategory
FROM table
) select count(ownerID),summaryCategory from cte group by summaryCategory

Using Limit on Distinct group by values psql

Suppose I have a table that looks like this or maybe I am going nowhere.
create table customers (id text, name text, number int, useless text);
With values
insert into customers (id, name, number, useless)
values
('1','apple',1, 'a'),
('2','banana',3, 'b'),
('3','pear',2, 's'),
('4','apple',1,'e'),
('5','banana',3,'s'),
('6','cherry',3, 'a'),
('7','cherry',4, 's'),
('8','apple',2, 'd'),
('9','banana',4, 'c'),
('10','pear',5, 'e');
My failed psql query is this.
select id, name, number, useless
from customers
where number < 4
group by customers.name limit 2
the query i want to use that it returns first 2 unique grouped by customers.name. Not the first 2 rows
In the end I want it to return
('1','apple',1, 'a'),
('4','apple',1,'e'),
('8','apple',2, 'd'),
('2','banana',3, 'b'),
('5','banana',3,'s'),
so it returns the first 2 grouped names.
How can I make this query?
Thank you.
Edit:
this query is my second try I know I am kinda close.
select t.id, t.name, t.ranking
from (
SELECT id, name, dense_rank() OVER (order by name) as
ranking
FROM customers
group by name
) t
where t.ranking < 3
try this:
select id, name, number, useless
from customers
where name in (
select name
from customers
where number < 4
group by customers.name
order by name limit 2
)
| id | name | number | useless |
|----|--------|--------|---------|
| 1 | apple | 1 | a |
| 2 | banana | 3 | b |
| 4 | apple | 1 | e |
| 5 | banana | 3 | s |
| 8 | apple | 2 | d |
| 9 | banana | 4 | c |
SQL Fiddle DEMO
The group by customers.name function do not order your output, just group them by the customers.name, what you want to do is to order the group right? So what i think you want to do is:
select id, name, number, useless
from customers
group by name
order by name []*
*[asc/desc] depends of what order you want to do:
asc - ascendent,
desc - descendent
Hope it helps you.
You can use dense_rank() as:
SELECT * FROM (
SELECT DENSE_RANK() OVER (order by name) AS rank, temp.*
FROM customers temp WHERE number < 4) data
WHERE data.rank <= 2
| rank| id| name | number | useless |
|-----|---|--------|--------|---------|
| 1 | 4 | apple | 1 | e |
| 1 | 1 | apple | 1 | a |
| 1 | 8 | apple | 2 | d |
| 2 | 5 | banana | 3 | s |
| 2 | 2 | banana | 3 | b |