Sorry for the bad title - I simply do not know what to call the thing I want to do.
Here it goes:
In MS SQL Server 2008
I have a temp table with 4000+ rows created with the WITH statement looking like this:
ID (varchar) DATE (int)
AB1135000097 | 20151221
AB1135000097 | 20160119
AB1135000097 | 20160219
AB1135001989 | 20120223
AB1135001989 | 20120323
AB1135001989 | 20120423
.
.
.
I want to pair the data in date-ranges based on DATE.
AB1135000097 | 20151221 | 20160119
AB1135000097 | 20160119 | 20160219
AB1135001989 | 20120223 | 20120323
AB1135001989 | 20120323 | 20120423
Does this action have a name ? (I will add tags to the post when I know what I'm asking for)
Assumed schema
I am assuming that your table is like:
CREATE TABLE "TABLE"
(
tag CHAR(1) NOT NULL,
value INTEGER NOT NULL,
PRIMARY KEY(tag, value)
);
I really shouldn't have to guess the schema though.
Possible answer
Superficially, you might be after:
SELECT t1.tag, t1.value, t2.value
FROM "TABLE" AS t1
JOIN "TABLE" AS t2
ON t1.tag = t2.tag AND t2.value = t1.value + 1
ORDER BY t1.tag, t1.value;
This joins the table with itself, combining rows where the tag column values (A, B, ...) are the same, and where the value column in one row is one more than the value column in the other.
On the other hand, if you add a row ('A', 5) to the table and expect it to appear in the output as part of a row ('A', 3, 5), then the query is much harder to write without using OLAP features.
if you are using Oracle database then you can refer following query to solve this question -
with t as
(
SELECT 'A' Col1, 1 Col2
FROM Dual
UNION ALL
SELECT 'A' Col1, 2 Col2
FROM Dual
UNION ALL
SELECT 'A' Col1, 3 Col2
FROM Dual
UNION ALL
SELECT 'B' Col1, 4 Col2
FROM Dual
UNION ALL
SELECT 'B' Col1, 5 Col2
FROM Dual
UNION ALL
SELECT 'B' Col1, 6 Col2 FROM Dual
)
SELECT *
FROM (SELECT Col1,
Col2,
Lead(Col1) Over(ORDER BY Col1, Col2) Col3,
Lead(Col2) Over(ORDER BY Col1, Col2) Col4
FROM t --(your table name)
ORDER BY Col1, Col2)
WHERE Col1 = Col3
as I don't have your table name and table structure I have created one temp table in Query itself.
you need to change From t to From with your table name . .. please change col1 and col2 column name also accordingly.
I found a solution to my problem. Inspired by Jonathan Leffler's solution. Thanks a lot!
It is based on adding row-numbers to the table ordered by ID and DATE, and then self-join with ROW+1 to get the next date as a second date column.
with
SCHEDULE as
( -- remove duplicates and NULL entries
select DISTINCT ID, DATE from TABLE1
where DATE IS NOT NULL
),
SCHEDULE_WITH_ROW as
(
select * from (
select DISTINCT ROW_NUMBER()
OVER (ORDER BY ID, DATE) AS
ROW, ID, DATE
from SCHEDULE) AS SCHED
)
select
S1.ID
, S1.DATE
, S2.DATE
from SCHEDULE_WITH_ROW S1
join SCHEDULE_WITH_ROW S2 on S2.ID = S1.ID and S1.ROW + 1 = S2.ROW
Related
I am trying to find a way if data is not found based on col1 of a table then search with other column value
SELECT * FROM TABLE
WHERE COL1='123'
IF NULL
THEN
SELECT * FROM TABLE
WHERE COL2='ABC';
Thanks
This a typical SQL select statement involving an OR expression.
SELECT * from TABLE WHERE Col1 = '123' or Col2 = 'ABC';
You want all rows that satisfy the first condition - but if no row matches, then you want all rows that satisfy the second condition.
I would adress this with a row limiting clause (available starting version 12c):
select *
from mytable
where 'ABC' in (col1, col2)
order by rank() over(order by case col1 = 'ABC' then 1 else 2 end)
fetch first 1 row with ties
This is more efficient than union all because it does not require two scans on the table.
You can use exists with union all :
select t.*
from table t
where col1 = 123 union all
select t.*
from table t
where col2 = 'abc' and
not exists (select 1 from table t1 where t1.col1 = 123);
If you are expecting only one row, you can use:
SELECT t.*
FROM TABLE t
WHERE COL1 = '123' OR COL2 = 'ABC'
ORDER BY (CASE WHEN COL1 = '123' THEN 1 ELSE 2 END)
FETCH FIRST 1 ROW ONLY;
With multiple possible rows in the result set, I would go for:
SELECT t.*
FROM TABLE t
WHERE COL1 = '123' OR
(COL2 = 'ABC' AND
NOT EXISTS (SELECT 1 FROM TABLE t2 WHERE t2.COL1 = '123');
I want to make a UNION query. The first SELECT of it is pretty straight, but on the second one I'd like to select all entries in a table, where the IDs are not present in a row of the first part.
Something like this:
SELECT * FROM a
UNION ALL
SELECT * FROM b
WHERE b.id NOT IN (LISTAGG(a.selected_id))
Of yourse, I can't use an aggregat function here. But I don't have an idea how to solve this. Is it even possible?
I'm sure I could do another subselect for the NOT IN clause, but I want to avoid this, as I think this will hit too much to performance.
I think you want something like this:
SELECT a.*
FROM a
UNION ALL
SELECT b.*
FROM b
WHERE NOT EXISTS (SELECT 1 FROM a WHERE b.id = a.selected_id);
If performance is an issue, you want an index on a(selected_id).
This assumes that the columns are the same in the two tables.
In general, you want to use NOT EXISTS with a subquery because it does what you expect when the subquery returns NULL values.
Why not
SELECT * FROM a
UNION ALL
SELECT *
FROM b
WHERE b.id NOT IN (SELECT a.id FROM a)
As Matthew suggested, the NOT IN option is safe to use if a.id is not nullable. Otherwise, a NOT EXISTS would be a better option:
WHERE NOT EXISTS (SELECT 1 FROM a WHERE b.id = a.id);
On the other hand, if it were just about IDs (without mentioning other columns from both tables), is it not just union instead of union all?
select id from a
union
select id from b
because your query says:
give me IDs from b, but not the ones that exist in a
union that with IDs from a
which is (b minus a) union all a
which is a union b
I might be wrong, though; try both options and compare results. Yet again, as Matthew has noted, that approach doesn't make much sense if other columns from both tables are involved.
but i still like to avoid going over the table twice. well, at least
if it is avoidable.
In your posted query,
SELECT * FROM a
UNION ALL
SELECT * FROM b
WHERE b.id NOT IN (LISTAGG(a.selected_id))
I am going to assume that selected_Id is not actually a column in your tables, but rather was your way of saying "the list of ids selected from table a, above".
I am also going to assume that a.id and b.id are both non-nullable, unique keys.
If all those assumptions hold true, you might try this approach:
SELECT nvl(a.id, b.id) id,
nvl(a.col1, b.col1) col1,
nvl(a.col2, b.col2) col2,
-- you get the idea...
FROM a
FULL OUTER JOIN b b ON b.id = a.id;
This approach is more typing, but should access each table only once.
Here is a full example:
create table matta ( id number, col1 varchar2(5), col2 varchar2(5) );
create table mattb ( id number, col1 varchar2(5), col2 varchar2(5) );
insert into matta ( id, col1, col2 ) VALUES ( 1, 'A1.1', 'A1.2');
insert into matta ( id, col1, col2 ) VALUES ( 2, 'A2.1', 'A2.2');
insert into matta ( id, col1, col2 ) VALUES ( 3, 'A3.1', 'A3.2');
insert into matta ( id, col1, col2 ) VALUES ( 4, 'A4.1', 'A4.2');
insert into mattb ( id, col1, col2 ) VALUES ( 3, 'B3.1', 'B3.2');
insert into mattb ( id, col1, col2 ) VALUES ( 4, 'B4.1', 'B4.2');
insert into mattb ( id, col1, col2 ) VALUES ( 5, 'B5.1', 'B5.2');
COMMIT;
SELECT nvl(a.id, b.id),
nvl(a.col1, b.col1),
nvl(a.col2, b.col2)
FROM matta a
FULL OUTER JOIN mattb b ON b.id = a.id
ORDER BY 1;
+----+------+------+
| ID | COL1 | COL2 |
+----+------+------+
| 1 | A1.1 | A1.2 |
| 2 | A2.1 | A2.2 |
| 3 | A3.1 | A3.2 |
| 4 | A4.1 | A4.2 |
| 5 | B5.1 | B5.2 |
+----+------+------+
One more option to try is the LEFT JOIN of b to a end exclude the matching rows, where b.id = a.selected_id:
SELECT a.* FROM a
UNION ALL
SELECT b.* FROM b
LEFT JOIN a ON b.id = a.selected_id
WHERE a.selected_id IS NULL;
I have a set of rows with many columns. For example,
ID | Col1 | Col2 | Col3 | Duplicate
------------------------------------
81 | 101 | 102 | 101 | YES
82 | 101 | 103 | 104 | NO
I need to calculate the "Duplicate" column. It is duplicate because it has the same value in Col1 and Col3. I know there is the LEAST function, which is similar to the MIN function but with columns. Does something similar to achieve this exists?
The approach I have in mind is to write all possible combinations in a case like this:
SELECT ID, col1, col2, col3,
CASE WHEN col1 = col2 or col1 = col3 or col2 = col3 then 1 else 0 end as Duplicate
FROM table
But, I wish to avoid that, since I have too many columns in some cases, and is very prone to errors.
What is the best way to solve this?
Hmmm. You are looking for within-row duplicates. This is painful. More recent versions of Oracle support lateral joins. But for just a handful of non-NULL columns, you can do:
select id, col1, col2, col3,
(case when col1 in (col2, col3) or col2 in (col3) then 1 else 0 end) as Duplicate
from t;
For each additional column, you need to add one more in comparison and update the other in-lists.
Something like this... note that in the lateral clause we still need to unpivot, but that is one row at a time - resulting in possibly much faster execution than simple unpivot and standard aggregation.
with
input_data ( id, col1, col2, col3 ) as (
select 81, 101, 102, 101 from dual union all
select 82, 101, 103, 104 from dual
)
-- End of simulated input data (for testing purposes only).
-- Solution (SQL query) begins BELOW THIS LINE.
select i.id, i.col1, i.col2, i.col3, l.duplicates
from input_data i,
lateral ( select case when count (distinct val) = count(val)
then 'NO' else 'YES'
end as duplicates
from input_data
unpivot ( val for col in ( col1, col2, col3 ) )
where id = i.id
) l
;
ID COL1 COL2 COL3 DUPLICATES
-- ---- ---- ---- ----------
81 101 102 101 YES
82 101 103 104 NO
You can do this by unpivoting and then counting the distinct values per id and checking if it equals the number of rows for that id. Equal means there are no duplicates. Then left join this result to the original table to caclulate the duplicate column.
SELECT t.*,
CASE WHEN x.id IS NOT NULL THEN 'Yes' ELSE 'No' END AS duplicate
FROM t
LEFT JOIN
(SELECT id
FROM
(SELECT *
FROM t
unpivot (val FOR col IN (col1,col2,col3)) u
) t
GROUP BY id
HAVING count(*)<>count(DISTINCT val)
) x ON x.id=t.id
The best way† is to avoid storing repeating groups of columns. If you have multiple columns that essentially store comparable data (i.e. a multi-valued attribute), move the data to a dependent table, and use one column.
CREATE TABLE child (
ref_id INT,
col INT
);
INSERT INTO child VALUES
(81, 101), (81, 102), (81, 101),
(82, 101), (82, 103), (82, 104);
Then it's easier to find cases where a value occurs more than once:
SELECT id, col, COUNT(*)
FROM child
GROUP BY id, col
HAVING COUNT(*) > 1;
If you can't change the structure of the table, you could simulate it using UNIONs:
SELECT id, col1, COUNT(*)
FROM (
SELECT id, col1 AS col FROM mytable
UNION ALL SELECT id, col2 FROM mytable
UNION ALL SELECT id, col3 FROM mytable
... for more columns ...
) t
GROUP BY id, col
HAVING COUNT(*) > 1;
† Best for the query you are trying to run. A denormalized storage strategy might be better for some other types of queries.
SELECT ID, col1, col2,
NVL2(NULLIF(col1, col2), 'Not duplicate', 'Duplicate')
FROM table;
If you want to compare more than 2 columns can implement same logic with COALESCE
I think you want to use fresh data that doesnot contains any duplicate values inside table if it right then use SELECT DISTINCT statement like
SELECT DISTINCT * FROM TABLE_NAME
It will conatins duplicate free data,
Note: It will also applicable for a particular column like
SELECT DISTINCT col1 FROM TABLE_NAME
I am trying to figure out how to use the values from a table as a select statement for example
table1 contains:
column- cl1
Value - numb
table2:
column - numb
values 1,2,3,4
i want to select cl1 (value: numb) and then use it to run a statement
select numb from table2
so far i have used
select (select cl1 from table1) from table2;
this returns numb 4 times but i want the actual values
the output that i expect is
1,2,3,4.
I want the query to select from table 1 which will return the field name and then use that field name(numb) as part of the select statement so expecting the end sql to look like:
select numb from table2;
However numb will be whatever is in table1;
You CAN do it, but you need to expand out your table to include by the list of possible columns that you are selecting with one row per column per source row. IF this is a large list of possibilities or a large data set....well, it ain't gonna be pretty.
For example:
With thedata as (
select 1 row_id, 11 col1, 12 col2, 13 col3 from dual union all
select 2 row_id, 21 col1, 22 col2, 23 col3 from dual union all
select 3 row_id, 31 col1, 32 col2, 33 col3 from dual union all
select 4 row_id, 41 col1, 42 col2, 43 col3 from dual )
, col_list as (
select 1 col_id, 'col1' col from dual union all
select 2 col_id, 'col2' col from dual union all
select 3 col_id, 'col3' col from dual )
select row_id, coldata
FROM (
-- here's where I have to mulitply the source data, generating one row for each possible column, and hard-coding that column to join to
SELECT row_id, 'col1' as col, col1 as coldata from thedata
union all
SELECT row_id, 'col2' as col, col2 as coldata from thedata
union all
SELECT row_id, 'col3' as col, col3 as coldata from thedata
) expanded_Data
JOIN col_list
on col_list.col = expanded_data.col
where col_id = :your_id;
Set the id to 2 and get:
ROW_ID COLDATA
1 12
2 22
3 32
4 42
So yes it can be done, but not truly dynamically as you need to be fully aware before-hand and hard-code the possible column name values that you are pulling from your table. If you need a truly dynamic select that may pick any column, or from any table, then you need to build your query dynamically and EXECUTE IMMEDIATE.
Edit - Add this caveat:
I should add also that this only works if all of the possible columns grabbed are of the same datatype, or you will need to cast them all to a common data type.
You can use a variable to store the value of numb, then reuse it in your SELECT statement, like this:
DECLARE #numb int
SET #numb = (SELECT cl1 from table2)
SELECT * from stat1 s WHERE s.numb = #numb
I am reworking some tables from a screwed up database. A few of the tables had the same data with different table names, and each one of them also had similar data but different column names. Anyway, this is a weird request but this has to be down like this.
I need to pivot rows up to simulate one row so I can create one record from two different tables.
I have attached a photo. The table on the left will pull a single row and the table on the left will supply 1 - n rows based on the id from the left table. I need to pivot the rows up to simulate one row and create one record with the two results.
From my checking online the pivot seems to be the way to go but it seems to want me to group or do some type of aggregating.
What is the best way to go about doing this?
table1 ---Produces one row
table1id | col1 | col2 | col3
1 Wow Wee Zee
table2 ---Produces 1 - n rows
table2id | table1id | col1 | col2 | col3
1 1 sock cloth sup
2 1 bal baa zak
3 1 x y fooZ
needs to look like this (the below is not column names, they're the result set)
Woo,wee,zee,sock,cloth,sup,bla,baaa,zak,x,y,fooZ
If using MySQL:
SELECT a.table1id, GROUP_CONCAT(a.col) AS col_values
FROM
(
SELECT table1id, col1 col FROM table1 UNION ALL
SELECT table1id, col2 FROM table1 UNION ALL
SELECT table1id, col3 FROM table1 UNION ALL
SELECT table1id, col1 FROM table2 UNION ALL
SELECT table1id, col2 FROM table2 UNION ALL
SELECT table1id, col3 FROM table2
) a
GROUP BY a.table1id
SQLFiddle Demo
If using SQL-Server:
SELECT a.table1id, b.colnames
FROM table1 a
CROSS APPLY
(
SELECT STUFF((
SELECT ',' + aa.col
FROM
(
SELECT table1id, col1 col FROM table1 UNION ALL
SELECT table1id, col2 FROM table1 UNION ALL
SELECT table1id, col3 FROM table1 UNION ALL
SELECT table1id, col1 FROM table2 UNION ALL
SELECT table1id, col2 FROM table2 UNION ALL
SELECT table1id, col3 FROM table2
) aa
WHERE aa.table1id = a.table1id
FOR XML PATH('')
), 1, 1, '') AS colnames
) b
SQLFiddle Demo