I'm new to SQL queries so hopes this question isn't stupid.
I got two tables like this:
Table 1:
Name
Value
Count
global
g
1
domain
x
2
domain
y
1
agg
ba
1
Table 2:
Name
Value
Count
global
g
1
domain
z
1
agg
bb
1
I need to get this kind of table - which is consist of all rows without duplications, and the global row should changed it's count to the sum of the 'domain' rows from the first table only:
Table 3:
Name
Value
Count
global
g
3
domain
x
2
domain
y
1
domain
z
1
agg
ba
1
agg
bb
1
is this kind of operation is possible?
demo:db<>fiddle
SELECT * FROM table1
WHERE "Name" <> 'global' -- 1
UNION
SELECT -- 2
'global',
'g',
SUM("Count")
FROM table1
WHERE "Name" = 'domain'
UNION
SELECT * FROM table2
WHERE "Name" <> 'global' -- 1
Union both tables without the global row
Create a new global row for the expected sum of the table1 domain records. Union it as well.
Try this out
SELECT x.name, x.total_val, sum(occurence)
FROM (SELECT name, total_val, occurence FROM test union all select name, total_val, occurence from test2) x
group by x.name, x.total_val
You can check on this db fiddle as well test case
Related
Would it be possible to add more rows base on Keyword string in SQL ?
table A
PID PromotionName
1 OUT_EC_D10_V500K_FamilyCare_PROCO
2 OUT_EC_D5_V50K_Lunchbox_PROCO
3 OUT_EC_D5_V50K_PROCO
table B
promotion_code itm_name quantity
Lunchbox Item name 1 1
FamilyCare Item name 2 1
FamilyCare Item name 3 1
BUY1FREE6 Item name 4 1
HiSummer Item name 5 1
FamilyCare Item name 6 1
Example:
SELECT * FROM A where pid = '1';
Output of the SQL should be -
PID PromotionName Itm_name quantity
1 OUT_EC_D10_V500K_FamilyCare_PROCO
2 FamilyCare Item name 2 1
3 FamilyCare Item name 3 1
4 FamilyCare Item name 6 1
How to find string with keyword 'FamilyCare' in PromotionName of table A base on promotion_code of table B? If it exist it will add more rows in output
Any help with the SQL?
Here is how you can achieve this:
SELECT PID,PromotionName, '' as Itm_name, NULL as quantity
FROM A
WHERE pid = '1'
UNION
SELECT PID, PROMOTION_NAME, Itm_name, quantity
FROM
(SELECT * FROM A inner join B on a.promotionName LIKE '%'||b.promotion_name||'%')
WHERE pid='1'
You have to update your pid in both the places (before and after UNION).
Notice that tables were joined using LIKE operator with % before and after the word. Hence this joins if a part of a string is present in another column.
db<>fiddle link here
An option would be starting to construct a subquery factoring along with joining tables through a.promotionName LIKE '%'||b.promotion_code||'%' condition while filtering by b.promotion_code = 'FamilyCare', then add another query to combine the result sets by UNION ALL, and then enumerate with an id column by ROW_NUMBER() analytic function such as
WITH ab AS
(
SELECT a.*, b.*
FROM a
JOIN b
ON a.promotionName LIKE '%'||b.promotion_code||'%'
WHERE b.promotion_code = 'FamilyCare'
), ab2 AS
(
SELECT promotion_code, itm_name, quantity
FROM ab
UNION ALL
SELECT DISTINCT promotionName, NULL, NULL
FROM ab
)
SELECT ROW_NUMBER() OVER (ORDER BY itm_name NULLS FIRST) AS pid,
a.*
FROM ab2 a
if there's mismatch for the topmost query, then no row will be returned. eg. that query will check for the existence for the literal you provide
Demo
I have a table with the following columns:
ID_group, ID_elements
For example with the following records:
1, 1
1, 2
2, 2
2, 4
2, 5
2, 6
3, 7
And I have sets of the elements, for example: 1,2,5; 1,5,2; 1,2,4; 2,7;
I need to check (true or false) that exist a common group for the pairs of adjacent elements.
For example elements:
1,2,5 -> true [i.e. elements 1,2 has common group 1 and elements 2,5 has common group 2]
1,5,2 -> false [i.e. 1,5 do not have a common group unlike 5,2 (but the result is false due to 1,5 - false)]
1,2,4 -> true
2,7 -> false
First, we need a list of pairs. We can get this by taking your set as an array, turning each element into a row with unnest and then making pairs by matching each row with its previous row using lag.
with nums as (
select *
from unnest(array[1,2,5]) i
)
select lag(i) over() a, i b
from nums
offset 1;
a | b
---+---
1 | 2
2 | 5
(2 rows)
Then we join each pair with each matching row. To avoid counting duplicate data rows twice, we count only the distinct rows.
with nums as (
select *
from unnest(array[1,2,5]) i
), pairs as (
select lag(i) over() a, i b
from nums
offset 1
)
select
count(distinct(id_group,id_elements)) = (select count(*) from pairs)
from pairs
join foo on foo.id_group = a and foo.id_elements = b;
This works on any size array.
dbfiddle
Your query to check if elements in a set evaluate to true or not can be done via procedures/function. Set representation can be taken as a string and then splitting it to substring then returning the required result can use a record for multiple entries. For sql query, below is a sample that can be used as a workaround, you can try changing the below query based on your requirement.
select case when ( Select count(*)
from ( SELECT
id_group, count(distinct id_elements)
from table where
id_group
in (1,2,5)
group by ID_group having
id_elements
in (1,2,5)) =3 ) then "true" else "false"
end) from table;
#Schwern, thank you, it helped. But I have changed the condition join foo on foo.id_group = a, because as I understand, a is element's ID, not group's. I have changed the following section:
join foo fA on fA.id_elements = a
join foo fB on fB.id_elements = b and fA.group_id = fB.group_id;
I want to union 2 tables, but get the error
proc sql;
select * from Table1
outer union corr
select * from table2;
But get the error:
ERROR: The type of column EntryId from the left hand side of the OUTER UNION set operation is
different from EntryId on the right hand side
If I understand this correct and based on UNION ALL two SELECTs with different column types - expected behaviour?, the first column is a different data type and cannot proceed with the union (which is true)
RecordID num label='RecordID' format=20. informat=20.
and
RecordID num label='RecordID' format=11. informat=11.
BUT, there is a column I want to use which has the same format
Pseu char(64) label='Pseu' format=$64. informat=$64.
Pseu char(64) label='Pseu' format=$64. informat=$64.
and in each table they are columns 3 and 4.
Is there a way to union these table together using that column as the reference, as opposed to the original?
I tried to no avail:
proc sql;
select * from Table1
outer union corr
select * from table2
on Table1.Pseu=Table2.Pseu;
ERROR: Found "on" when expecting ;
It follows from the OUTER UNION CORRESPONDING example given on http://support.sas.com/documentation/cdl/en/proc/61895/HTML/default/viewer.htm#a002473694.htm, and is here based on what I want:
table1
R y p
1 A 100
2 B 101
3 R 102
table2
R z p
4 A 102
5 R 103
6 T 104
MERGED
p R y R z
100 1 A
101 2 B
102 3 R 4 A
103 5 R
104 6 T
Something like this perhaps:
proc sql;
select * from Table1
outer union corr
select p, r as r2, z from table2
Having a column alias for the column r.
Using a regular UNION:
select p, r, y, null, null from Table1
outer union corr
select p, null, null, r as r2, z from table2
The answer supplied by my search and jarlh were correct.
The issues arose due to the size and number of columns in the data sets to be union'd. I had to make sure that there were no column names repeated in the union'd data sets (in my 600 total columns, some had similar names), so I had to rename columns
Fair warning: I'm new to using SQL. I do so on an Oracle server either via AQT or with SQL Developer.
As I haven't been able to think or search my way to an answer, I put myself in your able hands...
I'd like to combine data from table A (high quality data) with data from table B (fresh data) such that the entries from B are only included when the date stamp are later than those available from table A.
Both tables include entries from multiple entities, and the latest date stamp varies with those entities.
On the 4th of january, the tables may look something like:
A____________________________ B_____________________________
entity date type value entity date type value
X 1.jan 1 1 X 1.jan 1 2
X 1.jan 0 1 X 1.jan 0 2
X 2.jan 1 1 X 2.jan 1 2
Y 1.jan 1 1 (new entry)X 3.jan 1 1
Y 3.jan 1 1 Y 1.jan 1 2
Y 3.jan 1 2
(new entry)Y 4.jan 1 1
I have made an attempt at some code that I hope clarify my need:
WITH
AA AS
(SELECT entity, date, SUM(value)
FROM table_A
GROUP BY
entity,
date),
BB AS
(SELECT entity, date, SUM(value)
FROM table_B
WHERE date > ALL (SELECT date FROM AA)
GROUP BY
entity,
date
)
SELECT * FROM (SELECT * FROM AA UNION ALL SELECT * FROM BB)
Now, if the WHERE date > ALL (SELECT date FROM AA)would work seperately for each entity, I think have what I need.
That is, for each entity I want all entries from A, and only newer entries from B.
As the data in table A often differ from that of B (values are often corrected) I dont think I can use something like: table A UNION ALL (table B MINUS table A)?
Thanks
Essentially you are looking for entries in BB which do not exist in AA. When you are doing date > ALL (SELECT date FROM AA) this will not take into consideration the entity in question and you will not get the correct records.
Alternative is to use the JOIN and filter out all matching entries with AA.
Something like below.
WITH
AA AS
(SELECT entity, date, SUM(value)
FROM table_A
GROUP BY
entity,
date),
BB AS
(SELECT entity, date, SUM(value)
FROM table_B
LEFT OUTER JOIN AA
ON AA.entity = BB.entity
AND AA.DATE = BB.date
WHERE AA.date == null
GROUP BY
entity,
date
)
SELECT * FROM (SELECT * FROM AA UNION ALL SELECT * FROM BB)
I find your question confusing, because I don't know where the aggregation is coming from.
The basic idea on getting newer rows from table_b uses conditions in the where clause, something like this:
select . . .
from table_a a
union all
select . . .
from table_b b
where b.date > (select max(a.date) from a where a.entity = b.entity);
You can, of course, run this on your CTEs, if those are what you really want to combine.
Use UNION instead of UNION ALL , it will remove the duplicate records
SELECT * FROM (
SELECT *
FROM AA
UNION
SELECT *
FROM BB )
I have a table in my database:
Name | Element
1 2
1 3
4 2
4 3
4 5
I need to make a query that for a number of arguments will select the value of Name that has on the right side these and only these values.
E.g.:
arguments are 2 and 3, the query should return only 1 and not 4 (because 4 also has 5). For arguments 2,3,5 it should return 4.
My query looks like this:
SELECT name FROM aggregations WHERE (element=2 and name in (select name from aggregations where element=3))
What do i have to add to this query to make it not return 4?
A simple way to do it:
SELECT name
FROM aggregations
WHERE element IN (2,3)
GROUP BY name
HAVING COUNT(element) = 2
If you want to add more, you'll need to change both the IN (2,3) part and the HAVING part:
SELECT name
FROM aggregations
WHERE element IN (2,3,5)
GROUP BY name
HAVING COUNT(element) = 3
A more robust way would be to check for everything that isn't not in your set:
SELECT name
FROM aggregations
WHERE NOT EXISTS (
SELECT DISTINCT a.element
FROM aggregations a
WHERE a.element NOT IN (2,3,5)
AND a.name = aggregations.name
)
GROUP BY name
HAVING COUNT(element) = 3
It's not very efficient, though.
Create a temporary table, fill it with your values and query like this:
SELECT name
FROM (
SELECT DISTINCT name
FROM aggregations
) n
WHERE NOT EXISTS
(
SELECT 1
FROM (
SELECT element
FROM aggregations aii
WHERE aii.name = n.name
) ai
FULL OUTER JOIN
temptable tt
ON tt.element = ai.element
WHERE ai.element IS NULL OR tt.element IS NULL
)
This is more efficient than using COUNT(*), since it will stop checking a name as soon as it finds the first row that doesn't have a match (either in aggregations or in temptable)
This isn't tested, but usually I would do this with a query in my where clause for a small amount of data. Note that this is not efficient for large record counts.
SELECT ag1.Name FROM aggregations ag1
WHERE ag1.Element IN (2,3)
AND 0 = (select COUNT(ag2.Name)
FROM aggregatsions ag2
WHERE ag1.Name = ag2.Name
AND ag2.Element NOT IN (2,3)
)
GROUP BY ag1.name;
This says "Give me all of the names that have the elements I want, but have no records with elements I don't want"