I have a temp table with many rows (#TümDATA). I INSERT it's rows into another temp table (#GrupTOT) with GROUP BY clause. But I'm stuck here, I need to give the rows a sequential number after they are GROUPED.
Here is my SQL:
INSERT INTO #GrupTOT(AY, BLK, DRE, TOT)
SELECT J.AY, J.BLK, J.DRE, SUM(J.BORÇ)
FROM #TümDATA J
GROUP BY J.AY, J.BLK, J.DRE
You can try SELECT INTO using an IDENTITY column. This will create the new temporary table #GrupTOT.
Here is a Fiddle example.
SELECT SeqNo = identity(int,1,1), --Identity column
AY = J.AY
BLK = J.BLK,
DRE = J.DRE,
TOT = SUM(J.BORÇ)
INTO #GrupTOT
FROM #TümDATA J
GROUP BY J.AY, J.BLK, J.DRE;
--SELECT * FROM #GrupTOT
You can use ROW_NUMBER to get a number based on an ORDER BY. Or you could add an IDENTITY column to autoincrement a number on insert if that is what you want.
The ROW_NUMBER approach:
WITH CTE AS
(
SELECT Col1, Col2, Col3, Count(*) as [COUNT]
FROM dbo.Table1
GROUP BY Col1, Col2, Col3
)
INSERT INTO dbo.Table2
SELECT RowNum = ROW_NUMBER() OVER ( ORDER BY Col1, Col2, Col3, [COUNT] DESC ),
Col1, Col2, Col3, [COUNT]
FROM CTE
Try this: (Works in Oracle)
WITH ORDERS
AS (SELECT
TO_DATE ( '2013-09-18 00:00:01',
'YYYY-MM-DD HH24:MI:SS' )
AS THE_DATE,
'John' AS NAME
FROM
DUAL
UNION ALL
SELECT
TO_DATE ( '2013-09-19 00:00:01',
'YYYY-MM-DD HH24:MI:SS' )
AS THE_DATE,
'James' AS NAME
FROM
DUAL
UNION ALL
SELECT
TO_DATE ( '2013-09-20 00:00:01',
'YYYY-MM-DD HH24:MI:SS' )
AS THE_DATE,
'John' AS NAME
FROM
DUAL
UNION ALL
SELECT
TO_DATE ( '2013-09-20 00:00:02',
'YYYY-MM-DD HH24:MI:SS' )
AS THE_DATE,
'John' AS NAME
FROM
DUAL
UNION ALL
SELECT
TO_DATE ( '2013-09-20 00:00:03',
'YYYY-MM-DD HH24:MI:SS' )
AS THE_DATE,
'John' AS NAME
FROM
DUAL
UNION ALL
SELECT
TO_DATE ( '2013-09-20 00:00:04',
'YYYY-MM-DD HH24:MI:SS' )
AS THE_DATE,
'John' AS NAME
FROM
DUAL
UNION ALL
SELECT
TO_DATE ( '2013-09-21 16:00:01',
'YYYY-MM-DD HH24:MI:SS' )
AS THE_DATE,
'Jennifer' AS NAME
FROM
DUAL)
SELECT
THE_DATE,
NAME,
ROWNUM
FROM
(SELECT
TRUNC ( THE_DATE ) THE_DATE,
NAME,
COUNT ( 1 )
FROM
ORDERS
GROUP BY
TRUNC ( THE_DATE ),
NAME);
Original Data:
9/18/2013 12:00:01 AM John
9/19/2013 12:00:01 AM James
9/20/2013 12:00:01 AM John
9/20/2013 12:00:02 AM John
9/20/2013 12:00:03 AM John
9/20/2013 12:00:04 AM John
9/21/2013 4:00:01 PM Jennifer
Result:
9/21/2013 Jennifer 1
9/19/2013 James 2
9/20/2013 John 3
9/18/2013 John 4
You can use the ROW_NUMBER() function.
E.g.
WITH q AS (
SELECT field_a
FROM SomeTable
GROUP BY field_a
)
SELECT ROW_NUMBER() OVER(ORDER BY field_a) AS row_num,
field_a
FROM q
Related
I have the following SQL statement that I would like to convert to use an EXISTS. How would this be done?
with Sales as (
select 'Office Supplies' Category , 2014 Year,22593.42 Profit UNION all
select 'Technology', 2014, 21492.83 UNION all
select 'Furniture', 2014, 5457.73 UNION all
select 'Office Supplies', 2015, 25099.53 UNION all
select 'Technology', 2015, 33503.87 UNION all
select 'Furniture', 2015, 50000.00 UNION all
select 'Office Supplies', 2016, 35061.23 UNION all
select 'Technology', 2016, 39773.99 UNION all
select 'Furniture', 2016, 6959.95
)
select Category, Profit - LAG(Profit) OVER (PARTITION BY Category ORDER BY Year) Diff
FROM Sales where 1=1 qualify Diff < 0
In other words, I want the query to be something like:
SELECT * FROM tbl WHERE EXISTS (...)
If LAG exists then you can use a sub-query instead of Teradata's QUALIFY.
SELECT Category, `Year`, Profit
, PreviousProfit
, (Profit - PreviousProfit) AS Diff
FROM
(
SELECT Category, `Year`, Profit
, LAG(Profit) OVER (PARTITION BY Category ORDER BY `Year`) AS PreviousProfit
FROM Sales AS sale
) AS sale
WHERE EXISTS (
SELECT 1
FROM Sales AS sale2
WHERE sale2.Category = sale.Category
AND sale2.Year = sale.Year - 1
AND sale2.Profit > sale.Profit
)
AND PreviousProfit > Profit
The EXISTS isn't really needed then.
let say we have a table with below column details.
now I wanted to extract only those custID's where either SSN or DL number is same for different custid
table
***CustID SSN DL***
1111 112331 DL1234
1112 113096 DL0987
1113 113861 DL1234
1114 112331 DL2315
1115 111104 DL5443
1115 111104 DL5443
in this I only want 1111,1113 & 1114
enter image description here
In Oracle, you can do it in a single table scan using analytic functions:
SELECT CustID
FROM (
SELECT CustID,
COUNT(*) OVER (PARTITION BY SSN, DL) AS num_ssl_dn,
COUNT(*) OVER (PARTITION BY SSN) AS num_ssn,
COUNT(*) OVER (PARTITION BY DL) AS num_dl
FROM table_name
)
WHERE num_ssn > num_ssl_dn
OR num_dl > num_ssl_dn;
Which, for the sample data:
CREATE TABLE table_name ( CustID, SSN, DL ) AS
SELECT 1111, 112331, 'DL1234' FROM DUAL UNION ALL
SELECT 1112, 113096, 'DL0987' FROM DUAL UNION ALL
SELECT 1113, 113861, 'DL1234' FROM DUAL UNION ALL
SELECT 1114, 112331, 'DL2315' FROM DUAL UNION ALL
SELECT 1115, 111104, 'DL5443' FROM DUAL UNION ALL
SELECT 1115, 111104, 'DL5443' FROM DUAL;
Outputs:
CUSTID
1111
1114
1113
sqlfiddle here
You can use exists:
select t.*
from t
where exists (select 1
from t t2
where t2.ssn = t.ssn and t2.custid <> t.custid
) or
exists (select 1
from t t2
where t2.dl = t.dl and t2.custid <> t.custid
) ;
This intentionally uses two exists instead of or in a single subquery so the optimizer can use indexes on (ssn) and (dl) if available.
Assuming the table name is table_name, you can do it like this:
select custid
from (
select custid,
count(distinct custid) over (partition by ssn) ssn_ct,
count(distinct custid) over (partition by dl) dl_ct
from table_name
)
where ssn_ct > 1 or dl_ct > 1
;
Right now I just have an aggregate of how many days a user has worked. I'm trying to change this query to most continuous days worked.
Where u12345 would be 4 and u1 would be 2.
Is this possible to do with a BigQuery statement?
EDIT I am Kind of close with the following query but my u1 is getting 3 instead of 2.
SELECT MIN(e.timestamp) as date_created, e.uid, COUNT(e.uid) + 1 AS streak
FROM OnSite e
LEFT JOIN OnSite ee
ON e.uid = ee.uid
AND DATE(e.timestamp) = DATE(DATE_ADD(ee.timestamp, INTERVAL -1 DAY))
WHERE ee.uid IS NOT NULL
GROUP BY e.uid;
Schema (MySQL v5.7)
CREATE TABLE OnSite
(`uid` varchar(55), `worksite_id` varchar(55), `timestamp` datetime)
;
INSERT INTO OnSite
(`uid`, `worksite_id`, `timestamp`)
VALUES
("u12345", "worksite_1", '2019-01-01'),
("u12345", "worksite_1", '2019-01-02'),
("u12345", "worksite_1", '2019-01-03'),
("u12345", "worksite_1", '2019-01-04'),
("u12345", "worksite_1", '2019-01-06'),
("u1", "worksite_1", '2019-01-01'),
("u1", "worksite_1", '2019-01-02'),
("u1", "worksite_1", '2019-01-05'),
("u1", "worksite_1", '2019-01-06')
;
Query #1
SELECT uid, COUNT(DISTINCT timestamp) Total
FROM OnSite
GROUP BY uid;
| uid | Total |
| ------ | ----- |
| u1 | 4 |
| u12345 | 5 |
View on DB Fiddle
Below is for BigQuery Standard SQL
In case if you are interested in max consecutive days of the users on the same worksite:
#standardSQL
SELECT uid, MAX(consecuitive_days) max_consecuitive_days
FROM (
SELECT uid, grp, COUNT(1) consecuitive_days
FROM (
SELECT uid,
COUNTIF(step > 1) OVER(PARTITION BY uid, worksite_id ORDER BY ts) grp
FROM (
SELECT uid, worksite_id, ts,
DATE_DIFF(ts, LAG(ts) OVER(PARTITION BY uid, worksite_id ORDER BY ts), DAY) step
FROM `project.dataset.table`
)
) GROUP BY uid, grp
) GROUP BY uid
In case if worksite does not matter and you are looking just for max consecutive days:
#standardSQL
SELECT uid, MAX(consecuitive_days) max_consecuitive_days
FROM (
SELECT uid, grp, COUNT(1) consecuitive_days
FROM (
SELECT uid,
COUNTIF(step > 1) OVER(PARTITION BY uid ORDER BY ts) grp
FROM (
SELECT uid, ts,
DATE_DIFF(ts, LAG(ts) OVER(PARTITION BY uid ORDER BY ts), DAY) step
FROM `project.dataset.table`
)
) GROUP BY uid, grp
) GROUP BY uid
You can test, play any of above with he sample data from your question as in below example
#standardSQL
WITH `project.dataset.table` AS (
SELECT 'u12345' uid, 'worksite_1' worksite_id, DATE '2019-01-01' ts UNION ALL
SELECT 'u12345', 'worksite_1', '2019-01-02' UNION ALL
SELECT 'u12345', 'worksite_1', '2019-01-03' UNION ALL
SELECT 'u12345', 'worksite_1', '2019-01-04' UNION ALL
SELECT 'u12345', 'worksite_1', '2019-01-06' UNION ALL
SELECT 'u1', 'worksite_1', '2019-01-01' UNION ALL
SELECT 'u1', 'worksite_1', '2019-01-02' UNION ALL
SELECT 'u1', 'worksite_1', '2019-01-05' UNION ALL
SELECT 'u1', 'worksite_1', '2019-01-06'
)
SELECT uid, MAX(consecuitive_days) max_consecuitive_days
FROM (
SELECT uid, grp, COUNT(1) consecuitive_days
FROM (
SELECT uid,
COUNTIF(step > 1) OVER(PARTITION BY uid ORDER BY ts) grp
FROM (
SELECT uid, ts,
DATE_DIFF(ts, LAG(ts) OVER(PARTITION BY uid ORDER BY ts), DAY) step
FROM `project.dataset.table`
)
) GROUP BY uid, grp
) GROUP BY uid
with result:
Row uid max_consecuitive_days
1 u12345 4
2 u1 2
does this fit for you?
set #gr=1;
select uid, max(cnt) max_cnt from (
select uid, grp, count(*) cnt from (
select uid,
case when ifnull(DATE_ADD(oldDate, INTERVAL 1 DAY), timestamp)= timestamp then
#gr
else
#gr := #gr +1
end grp
from
(
SELECT
uid,
timestamp,
lag(timestamp) over (partition by uid order by timestamp asc) as oldDate
FROM OnSite
) t
)t2
group by uid, grp
)t3
group by uid
Result
| uid | max_cnt |
| ------ | ------- |
| u1 | 2 |
| u12345 | 4 |
DB Fiddle
For example:
pk_ref fk
====== ===
1 a
1 b
1 c
2 a
2 b
2 d
How do I do a query like the "pseudo" query:
select distinc pk_ref
where fk in all('a', 'c');
The return query result must match all given values for the foreign key in the list.
The result should be:
1
While the following select must not return any records.
select distinc pk_ref
where fk in all('a', 'c', 'd');
How do I do that?
Try this
select pk_ref
from yourtable
group by pk_ref
having count(case when fk = 'a', then 1 end) >= 1
and count(case when fk = 'c' then 1 end) >= 1
To do it dynamically. (considering you are using SQL SERVER)
Create a split string function and pass the input as comma separated values
Declare #input varchar(8000)= 'a,c',#cnt int
set #cnt = len(#input)-len(replace(#input,',','')) + 1
select pk_ref
from yourtable
Where fk in (select split_values from udf_splitstring(#input , ','))
group by pk_ref
having count(Distinct fk) >= #cnt
You can create a split string function from the below link
https://sqlperformance.com/2012/07/t-sql-queries/split-strings
:list is the input list (bind variable). The difference of length() return values is the number of commas in the bind variable. This query, or something very close to it, should work in pretty much any DB product. Tested in Oracle.
select pk_ref
from tbl -- enter your table name here
where ',' || :list || ',' like '%,' || fk || ',%'
group by pk_ref
having count(distinct fk) = 1 + length(:list) - length(replace(:list, ',', ''))
If you can pass the IN operator values as Set, then you can do this as below
Schema:
SELECT * INTO #TAB FROM (
SELECT 1 ID, 'a' FK
UNION ALL
SELECT 1, 'b'
UNION ALL
SELECT 1, 'c'
UNION ALL
SELECT 2, 'a'
UNION ALL
SELECT 2, 'b'
UNION ALL
SELECT 2, 'd'
UNION ALL
SELECT 1, 'a'
)AS A
Used CTE to make 'a','c' as Set
;WITH CTE AS (
SELECT 'a' FK --Here 'a','c' passed as a Set through CTE
UNION
SELECT 'c'
)
,FINAL AS(
SELECT DENSE_RANK() OVER (PARTITION BY ID ORDER BY (FK))AS COUNT_ID, ID, FK
FROM #TAB where FK IN (select FK FROM CTE)
)
SELECT ID FROM FINAL WHERE COUNT_ID>=(SELECT COUNT( FK) FROM CTE)
Select pk_ref where fk='a' and pk_ref in (select pk_ref where fk='c' from yourtable) from yourtable;
or
select pk_ref where fk='a' from yourtable intersect select pk_ref where fk='c' from yourtable;
DECLARE #inputVariable VARCHAR(200) = 'a,b,c,d'
DECLARE #inputValue INT
DECLARE #tblInput TABLE
(
FK VARCHAR(100)
)
INSERT INTO #tblInput
SELECT SUBSTRING( #inputVariable+',',RN,1)
FROM (SELECT TOP 100 ROW_NUMBER() OVER(ORDER BY s.object_id) RN
FROM sys.objects s) s
where LEN(#inputVariable) >= RN
AND SUBSTRING(','+ #inputVariable,RN,1) = ','
SELECT #inputValue = COUNT(1) FROm #tblInput
--#inputVariable
DECLARE #tbl TABLE
(
ID INT,
FK VARCHAR(100)
)
INSERT INTO #tbl
SELECT 1 ID, 'a' FK
UNION ALL
SELECT 1, 'b'
UNION ALL
SELECT 1, 'c'
UNION ALL
SELECT 2, 'a'
UNION ALL
SELECT 2, 'b'
UNION ALL
SELECT 2, 'd'
UNION ALL
SELECT 1, 'a'
SELECT t.ID ,COUNT(DISTINCT t.FK)
FROM #tbl t
INNER JOIn #tblInput ti
ON t.FK = ti.FK
GROUP BY ID
HAVING COUNT(DISTINCT t.FK) = #inputValue
Say I have the following:
CREATE TABLE newtable AS (
SELECT #rownum:=#rownum+1 as rownum, name, age FROM (
SELECT name, age FROM clubAmembers
UNION
SELECT name, age FROM clubBmembers
)
) AS atable
How can I make it such that I can "stick in a new row at the beginning of the table" prior to the SELECT union such that it would start with:
rownum | name| age
1 | "Jordan" | 6 <-- This is an arbitrarily inserted record with name="Jordan" age="6" that is not a part of any of the clubAmembers or clubBmembers table.
The rest of the table (rownum 2 and onwards) would contain the actual result form the union with clubAmembers then clubBmembers.
Basically I am looking for:
CREATE TABLE
INSERT a row "Jordan" | 6
Perform select with union such that the rows after the first would start with "rownum=2", all the data from clubAmembers, etc.
How to best do this?
"At the beginning of the table" is not truly meaningful to relational databases because the order results are returned are not guaranteed until you use an ORDER BY clause, at which point the order on disk becomes a moot point anyway.
In your case, since you want to guarantee an order in your result clause (and therefore ordering #rownum, you will have to use ORDER BY. Something like:
CREATE TABLE newtable AS (
SELECT #rownum:=#rownum+1 as rownum, name, age
FROM (
SELECT 'Jordan' AS name, 6 AS age, 0 AS ord
UNION
SELECT name, age, 1 AS ord FROM clubAmembers
UNION
SELECT name, age, 1 AS ord FROM clubBmembers
ORDER BY ord
)
) AS atable
Note that at no point does this guarantee that rows in clubAmembers will have a lower rownum than rows in clubBmembers. If you want to guarantee that clubAmembers have a lower rownum, while keeping the semantics of UNION (versus UNION ALL), you can use the following:
CREATE TABLE newtable AS (
SELECT #rownum:=#rownum+1 as rownum, name, age
FROM (
SELECT 'Jordan' AS name, 6 AS age, 0 AS ord
UNION ALL
SELECT name, age, 1 AS ord FROM clubAmembers
UNION ALL
SELECT name, age, 2 AS ord FROM clubBmembers AS b
WHERE NOT EXISTS(SELECT 1 FROM clubAmembers AS a
WHERE a.name = b.name AND a.age = b.age)
ORDER BY ord
)
) AS atable
Note if {name, age} could be duplicated within the clubXmembers table, you will need to add DISTINCT:
...
SELECT DISTINCT name, age, 1 AS ord FROM clubAmembers
UNION ALL
...
As per the request in the comments, if you had a clubCmembers table, you would do:
CREATE TABLE newtable AS (
SELECT #rownum:=#rownum+1 as rownum, name, age
FROM (
SELECT 'Jordan' AS name, 6 AS age, 0 AS ord
UNION ALL
SELECT name, age, 1 AS ord FROM clubAmembers
UNION ALL
SELECT name, age, 2 AS ord FROM clubBmembers AS b
WHERE NOT EXISTS(SELECT 1 FROM clubAmembers AS a
WHERE a.name = b.name AND a.age = b.age)
SELECT name, age, 3 AS ord FROM clubCmembers AS c
WHERE NOT EXISTS(SELECT 1 FROM clubAmembers AS a
WHERE a.name = c.name AND a.age = c.age)
AND NOT EXISTS(SELECT 1 FROM clubBmembers AS b
WHERE b.name = c.name AND b.age = c.age)
ORDER BY ord
)
) AS atable
I'm not sure if I got it right. But why don't you just add another union like this:
CREATE TABLE newtable AS (
SELECT #rownum:=#rownum+1 as rownum, name, age FROM (
SELECT 1, "Jordan", 6
UNION ALL
SELECT name, age FROM clubAmembers
UNION ALL
SELECT name, age FROM clubBmembers
)
) AS atable
You can separate the create table statmenet from the insert statmenet:
Create the table (you must know which colums are gona be there)
Insert your 1st record (INSERT INTO .... Values(...))
Use your statement but with insert into instead of create table like: INSERT INTO YourNewTable.... Values(YourSubQuery) (Nr and tye of columns must match your subquery)
This should do, I believe:
CREATE TABLE newtable AS (
SELECT (#rownum:=IFNULL(#rownum,0)+1)+1 as rownum, name, age FROM (
SELECT name, age FROM clubAmembers
UNION
SELECT name, age FROM clubBmembers
) AS s
UNION ALL
SELECT 1, 'Jordan', 6
) AS atable
Demo at SQL Fiddle: http://sqlfiddle.com/#!2/ab825/6