I'd like to know if there is a more optimal query to get what I want from the database.
My database schema is as follows :
Table1:
(NUM_T1C1;ID_T1C2)
Table2:
(ID_T2C1;RES_T2C2)
First, I want to get a random row defined as:
SELECT NUM_T1C1, ID_T2C1
FROM (
SELECT T1.NUM_T1C1, T2.ID_T2C1, DBMS_RANDOM.VALUE
FROM TABLE1 T1, TABLE2 T2
WHERE T1.NUM_T1C1 IS NOT NULL
AND T2.RES_T2C2 IS NOT NULL
AND T1.ID_T1C2 = T2.ID_T2C1
ORDER BY 3
) WHERE ROWNUM = 1
I will call this query: QUERY1.
My question is as follows, I want to get a row as (random NUM_T1C1 != QUERY1.NUM_T1C1; ID_T2C1 == QUERY1.ID_T2C1), so I have tried:
SELECT NUM_T1C1, ID_T2C1
FROM (
SELECT R2.NUM_T1C1, R1.ID_T2C1, DBMS_RANDOM.VALUE
FROM (
SELECT NUM_T1C1, ID_T2C1
FROM (
SELECT T1.NUM_T1C1, T2.ID_T2C1, DBMS_RANDOM.VALUE
FROM TABLE1 T1, TABLE2 T2
WHERE T1.NUM_T1C1 IS NOT NULL
AND T2.RES_T2C2 IS NOT NULL
AND T1.ID_T1C2 = T2.ID_T2C1
ORDER BY 3
) WHERE ROWNUM = 1
) R1, TABLE1 R2
WHERE R2.NUM_T1C1 <> R1.NUM_T1C1
ORDER BY 3
) WHERE ROWNUM = 1
It's working, but I think this is not the optimal way to do so.
Is there a better way to get the expected result?
EDIT:
I found another way to get those random rows but i still don't know if it's optimal:
SELECT NUM_T1C1, ID_T2C1
FROM (
SELECT R1.NUM_T1C1, R2.ID_T2C1
FROM
(
SELECT T1.NUM_T1C1, T2.ID_T2C1, DBMS_RANDOM.VALUE
FROM TABLE1 T1, TABLE2 T2
WHERE T1.NUM_T1C1 IS NOT NULL
AND T2.T2C2 IS NULL
AND T1.ID_T1C2 = T2.ID_T2C1
ORDER BY 3
) R1,
(
SELECT T2.ID_T2C1, DBMS_RANDOM.VALUE
FROM TABLE1 T1, TABLE2 T2
WHERE T1.NUM_T1C1 IS NOT NULL
AND T2.T2C2 IS NOT NULL
AND T1.ID_T1C2 = T2.ID_T2C1
ORDER BY 2
) R2
) WHERE ROWNUM = 1
Here is an example :
Table1 Table2
+----------+---------+ +---------+----------+
| NUM_T1C1 | ID_T1C2 | | ID_T2C1 | RES_T2C2 |
+----------+---------+ +---------+----------+
| 23 | 5 | | 9 | NULL |
| 521 | 4 | | 4 | DG_513 |
| 71 | 7 | | 7 | FN_731 |
| 97 | 9 | | 5 | NULL |
+----------+---------+ +---------+----------+
Result would be one of those (select one randomly) :
+----------+---------+
| NUM_T1C1 | ID_T2C1 |
+----------+---------+
| 23 | 4 |
| 23 | 7 |
| 97 | 4 |
| 97 | 7 |
+----------+---------+
Here is one way with only a single (cross) join:
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE Table1 ( NUM_T1C1, ID_T1C2 ) AS
SELECT 23, 5 FROM DUAL UNION ALL
SELECT 521, 4 FROM DUAL UNION ALL
SELECT 71, 7 FROM DUAL UNION ALL
SELECT 97, 9 FROM DUAL;
CREATE TABLE Table2 ( ID_T2C1, RES_T2C2 ) As
SELECT 9, NULL FROM DUAL UNION ALL
SELECT 4, 'DG_513' FROM DUAL UNION ALL
SELECT 7, 'FN_731' FROM DUAL UNION ALL
SELECT 5, NULL FROM DUAL;
Query 1:
SELECT NUM_T1C1, ID_T2C1
FROM (
SELECT NUM_T1C1,
ID_T2C1,
COUNT( CASE WHEN T1.ID_T1C2 = T2.ID_T2C1 THEN 1 END )
OVER ( PARTITION BY T1.NUM_T1C1 ) AS num_matches
FROM Table1 T1
CROSS JOIN
(
SELECT *
FROM Table2
WHERE RES_T2C2 IS NOT NULL
) T2
ORDER BY DBMS_RANDOM.VALUE
)
WHERE num_matches = 0
--AND ROWNUM = 1
Results:
| NUM_T1C1 | ID_T2C1 |
|----------|---------|
| 23 | 7 |
| 97 | 4 |
| 97 | 7 |
| 23 | 4 |
Related
1 table
| tel_no | client_no |
|-------------|---------------|
| 01011111234 | aa011234 |
| 01022221234 | aa021234 |
2 table
| client_no | client_name |
|-----------|-------------|
| 1234 | kim |
3 table
| client_no | client_name |
| 1234 | wa |
I want to:
If the front of client_no of 1table starts with aa01, the client_name of 2table will be changed.
If the front of client_no of 1table starts with aa02, the client_name of 3table will be changed.
Expected result:
| tel_no | client_no | client_name |
|-------------|------------|-------------|
| 01011111234 | aa011234 | kim |
| 01022221234 | aa021234 | wa |
What an awful data model ...
Sample data:
SQL> with
2 t1 (tel_no, client_no) as
3 (select '01011111234', 'aa011234' from dual union all
4 select '01022221234', 'aa021234' from dual
5 ),
6 t2 (client_no, client_name) as
7 (select '1234', 'kim' from dual),
8 t3 (client_no, client_name) as
9 (select '1234', 'wa' from dual)
10 --
Query:
11 select a.tel_no, a.client_no,
12 case when substr(a.client_no, 1, 4) = 'aa01' then b.client_name
13 when substr(a.client_no, 1, 4) = 'aa02' then c.client_name
14 end client_name
15 from t1 a join t2 b on substr(a.client_no, 5) = b.client_no
16 join t3 c on substr(a.client_no, 5) = c.client_no;
TEL_NO CLIENT_N CLI
----------- -------- ---
01011111234 aa011234 kim
01022221234 aa021234 wa
SQL>
You can use UNION ALL to join 2 selective joins:
SELECT tel_no, t1.client_no, client_name
FROM t1 JOIN t2
ON t1.client_no = CONCAT('aa01',t2.client_no)
UNION ALL
SELECT tel_no, t1.client_no, client_name
FROM t1 JOIN t3
ON t1.client_no = CONCAT('aa02',t3.client_no);
I have two tables of events in bigquery that look like as follows. The main idea is two count the number of events in each table (are always pairs of event_id and user_id) and join them in a single table that for each pair in any table it tells the number of events.
table 1:
| event_id | user id |
| -------- | ------- |
| 1 | 1 |
| 2 | 1 |
| 2 | 3 |
| 2 | 5 |
| 1 | 1 |
| 4 | 7 |
table 2:
| event_id | user id |
| -------- | ------- |
| 1 | 1 |
| 3 | 1 |
| 2 | 3 |
I would like to get a table which has the number of events of each table:
| event_id | user id | num_events_table1 | num_events_table2 |
| -------- | ------- | ----------------- | ----------------- |
| 1 | 1 | 2 | 1 |
| 2 | 1 | 1 | 0 |
| 2 | 3 | 1 | 1 |
| 2 | 5 | 1 | 0 |
| 4 | 7 | 1 | 0 |
| 3 | 1 | 0 | 1 |
Any idea of how to do this with sql? I have tried this:
SELECT i1, e1, num_viewed, num_displayed FROM
(SELECT id as i1, event as e1, count(*) as num_viewed
FROM table_1
group by id, event) a
full outer JOIN (SELECT id as i2, event as e2, count(*) as num_displayed
FROM table_2
group by id, event) b
on a.i1 = b.i2 and a.e1 = b.e2
This is not getting exactly what I want. I amb getting i1 which are null and e1 that are null.
Consider below
#standardSQL
with `project.dataset.table1` as (
select 1 event_id, 1 user_id union all
select 2, 1 union all
select 2, 3 union all
select 2, 5 union all
select 1, 1 union all
select 4, 7
), `project.dataset.table2` as (
select 1 event_id, 1 user_id union all
select 3, 1 union all
select 2, 3
)
select event_id, user_id,
countif(source = 1) as num_events_table1,
countif(source = 2) as num_events_table2
from (
select 1 source, * from `project.dataset.table1`
union all
select 2, * from `project.dataset.table2`
)
group by event_id, user_id
if applied to sample data in your question - output is
If I understand correctly, the simplest method is to modify your query via a USING clause along with COALESCE():
SELECT id, event, COALESCE(num_viewed, 0), COALESCE(num_displayed, 0)
FROM (SELECT id, event, count(*) as num_viewed
FROM table_1
GROUP BY id, event
) t1 FULL JOIN
(SELECT id , event, COUNT(*) as num_displayed
FROM table_2
GROUP BY id, event
) t2
USING (id, event);
Note: This requires that the two columns used for the JOIN have the same name. If this is not the case, then you might still need column aliases in the subqueries.
One way is aggregate the union
select event_id, user id, sum(cnt1) cnt1, sum(cnt2) cnt2
from (
select event_id, user id, 1 cnt1, 0 cnt2
from table_1
union all
select event_id, user id, 0 cnt1, 1 cnt2
from table_2 ) t
group by event_id, user id
I am looking for a best approach to update 1 table from another table which has common multiple id's.
Below is the Scenario
Table1:
+----------+---------+
| masterid | childid |
+----------+---------+
| 1 | NULL |
+----------+---------+
| 1 | NULL |
+----------+---------+
| 1 | NULL |
+----------+---------+
| 2 | NULL |
+----------+---------+
| 2 | NULL |
+----------+---------+
| 1 | NULL |
+----------+---------+
Table2:
+----------+---------+
| masterid | childid |
+----------+---------+
| 1 | 2 |
+----------+---------+
| 1 | 3 |
+----------+---------+
| 1 | 4 |
+----------+---------+
| 2 | 9 |
+----------+---------+
| 2 | 8 |
+----------+---------+
| 1 | 5 |
+----------+---------+
I wish to update table1 comparing the masterid of table2 and corresponding childid should be be updated.
Solution1: Using ROW_NUMBER()
;
WITH cte
AS (SELECT *, ROW_NUMBER() OVER (ORDER BY (SELECT 1)) rn
FROM #t1),
cte1
AS (SELECT *, ROW_NUMBER() OVER (ORDER BY (SELECT 1)) rn
FROM #t2)
UPDATE t1
SET t1.childid = t2.childid
FROM cte AS t1
INNER JOIN cte1 AS t2
ON t2.masterid = t1.masterid
AND t2.rn = t1.rn
DEMO
How this can be achieved in different ways?
Sample Data
DECLARE #TAble1 AS TABLE (masterid INT , childid INT)
INSERT INTO #Table1
SELECT 1 , NULL UNION ALL
SELECT 1 , NULL UNION ALL
SELECT 1 , NULL UNION ALL
SELECT 2 , NULL UNION ALL
SELECT 2 , NULL UNION ALL
SELECT 1 , NULL
DECLARE #TAble2 AS TABLE (masterid INT , childid INT)
INSERT INTO #TAble2
SELECT 1, 2UNION ALL
SELECT 1, 3UNION ALL
SELECT 1, 4UNION ALL
SELECT 2, 9UNION ALL
SELECT 2, 8UNION ALL
SELECT 1, 5
This Update State is enough to update table Table1 No Need to use Any window function
SELECT * FROM #TAble1
UPDATE T1
SET T1.childid = T2.childid
FROM #TAble1 T1
INNER JOIN #TAble2 T2
ON T1.masterid = T2.masterid
SELECT * FROM #TAble1
Result Before Update
masterid childid
1 NULL
1 NULL
1 NULL
2 NULL
2 NULL
1 NULL
Result After Update
masterid childid
1 2
1 2
1 2
2 9
2 9
1 2
I have the Below Data in my Table.
| Id | FeeModeId |Name | Amount|
---------------------------------------------
| 1 | NULL | NULL | 20 |
| 2 | 1 | Quarter-1 | 5000 |
| 3 | NULL | NULL | 2000 |
| 4 | 2 | Quarter-2 | 8000 |
| 5 | NULL | NULL | 5000 |
| 6 | NULL | NULL | 2000 |
| 7 | 3 | Quarter-3 | 6000 |
| 8 | NULL | NULL | 4000 |
How to write such query to get below output...
| Id | FeeModeId |Name | Amount|
---------------------------------------------
| 1 | NULL | NULL | 20 |
| 2 | 1 | Quarter-1 | 5000 |
| 3 | 1 | Quarter-1 | 2000 |
| 4 | 2 | Quarter-2 | 8000 |
| 5 | 2 | Quarter-2 | 5000 |
| 6 | 2 | Quarter-2 | 2000 |
| 7 | 3 | Quarter-3 | 6000 |
| 8 | 3 | Quarter-3 | 4000 |
Since you are on SQL Server 2012... here is a version that uses that. It might be faster than other solutions but you have to test that on your data.
sum() over() will do a running sum ordered by Id adding 1 when there are a value in the column and keeping the current value for null values. The calculated running sum is then used to partition the result in first_value() over(). The first value ordered by Id for each "group" of rows generated by the running sum has the value you want.
select T.Id,
first_value(T.FeeModeId)
over(partition by T.NF
order by T.Id
rows between unbounded preceding and current row) as FeeModeId,
first_value(T.Name)
over(partition by T.NS
order by T.Id
rows between unbounded preceding and current row) as Name,
T.Amount
from (
select Id,
FeeModeId,
Name,
Amount,
sum(case when FeeModeId is null then 0 else 1 end)
over(order by Id) as NF,
sum(case when Name is null then 0 else 1 end)
over(order by Id) as NS
from YourTable
) as T
SQL Fiddle
Something that will work pre SQL Server 2012:
select T1.Id,
T3.FeeModeId,
T2.Name,
T1.Amount
from YourTable as T1
outer apply (select top(1) Name
from YourTable as T2
where T1.Id >= T2.Id and
T2.Name is not null
order by T2.Id desc) as T2
outer apply (select top(1) FeeModeId
from YourTable as T3
where T1.Id >= T3.Id and
T3.FeeModeId is not null
order by T3.Id desc) as T3
SQL Fiddle
Please try:
select
a.ID,
ISNULL(a.FeeModeId, x.FeeModeId) FeeModeId,
ISNULL(a.Name, x.Name) Name,
a.Amount
from tbl a
outer apply
(select top 1 FeeModeId, Name
from tbl b
where b.ID<a.ID and
b.Amount is not null and
b.FeeModeId is not null and
a.FeeModeId is null order by ID desc)x
OR
select
ID,
ISNULL(FeeModeId, bFeeModeId) FeeModeId,
ISNULL(Name, bName) Name,
Amount
From(
select
a.ID , a.FeeModeId, a.Name, a.Amount,
b.ID bID, b.FeeModeId bFeeModeId, b.Name bName,
MAX(b.FeeModeId) over (partition by a.ID) mx
from tbl a left join tbl b on b.ID<a.ID
and b.FeeModeId is not null
)x
where bFeeModeId=mx or mx is null
SELECT
T.ID,
ISNULL(T.FeeModeId,
(SELECT TOP 1 FeeModeId
FROM TableName AS T1
WHERE ID < T.ID AND FeeModeId IS NOT NULL
ORDER BY ID DESC)) AS FeeModeId,
ISNULL(Name,
(SELECT TOP 1 Name
FROM TableName
WHERE ID < T.ID AND Name IS NOT NULL
ORDER BY ID DESC)) AS Name,
T.Amount
FROM
TableName AS T
try this -
SELECT Id,
CASE
WHEN Feemodeid IS NOT NULL THEN
Feemodeid
ELSE
(SELECT Feemodeid
FROM Table_Name t_2
WHERE t_2.Id = (SELECT MAX(Id)
FROM Table_Name t_3
WHERE t_3.Id < t_1.Id
AND Feemodeid IS NOT NULL))
END Feemodeid,
CASE
WHEN NAME IS NOT NULL THEN
NAME
ELSE
(SELECT NAME
FROM Table_Name t_2
WHERE t_2.Id = (SELECT MAX(Id)
FROM Table_Name t_3
WHERE t_3.Id < t_1.Id
AND NAME IS NOT NULL))
END NAME,
Amount
FROM Table_Name t_1
id name
1 toto
2 NULL
3 NULL
4 titi
5 NULL
6 NULL
7 tutu
8 NULL
9 NULL
SELECT
id_table
,name
FROM
(
SELECT
T_01.id AS 'id_table'
,max(T_02.id) AS 'id_name'
FROM
names AS T_01
cross join
(
SELECT
id
,name
FROM
names
WHERE
name IS NOT NULL
) AS T_02
WHERE
T_02.id <= T_01.id
GROUP BY
T_01.id
) AS tt02
left join names
ON names.id = tt02.id_name
id_table name
1 toto
2 toto
3 toto
4 titi
5 titi
6 titi
7 tutu
8 tutu
9 tutu
I have a MySQL table similar to this:
| id | name | create_date |
---------------------------
| 1 | foo | 2003-03-11 |
| 2 | goo | 2003-04-27 |
| 3 | woo | 2004-10-07 |
| 4 | too | 2004-12-01 |
| 5 | hoo | 2005-04-20 |
| 6 | koo | 2006-01-12 |
| 7 | boo | 2006-04-17 |
| 8 | moo | 2006-08-19 |
I want to fetch all the latest yearly rows - one per year. So in the example above I'll get 2, 4, 5 and 8.
What's the right syntax?
Some of the other answers may work for you but this simple query does not require any joins
SELECT YEAR(create_date),
(SELECT id ORDER BY create_date DESC LIMIT 1)
FROM mytable
group by YEAR(create_date)
you can do something like
select * from table_name
where create_date in (
select max(create_date)
from table_name
group by year(create_date))
SELECT id FROM foo JOIN
(SELECT YEAR(create_date),MAX(create_date) AS md
FROM foo
GROUP BY YEAR(create_date)) as maxes
ON (create_date=md);
If you put an index on create_date, this will be fairly fast.
SELECT mi.*
FROM (
SELECT DISTINCT YEAR(created_date) AS dyear
FROM mytable
) md
JOIN mytable mi
ON mi.id =
(
SELECT id
FROM mytable ml
WHERE ml.create_date < CAST(CONCAT_WS('.', dyear + 1, 1, 1)) AS DATETIME)
ORDER BY
ml.create_date DESC
LIMIT 1
)
select id
from mytable
where not exists (
select * from mytable as T2
where T2.id = mytable.id
and T2.id >= year(created_date) + 1
)