SQL group by, let null value stay - sql

I have an SQL table for transfer histories like this:
ID | Date_out | Date_in
---+----------+----------
1 | 01.01.15 |
2 | 01.01.15 | 13.05.15
2 | 16.07.14 | 01.01.15
4 | 28.07.15 |
4 | 16.07.14 | 28.07.15
You can say that item with ID 2 was given away on 16.07.14 and returned on 01.01.15 and after that it went out again on 01.01.15 and returned back on 13.05.15. So it's in my office.
The item with ID 4 was given away on 16.07.14 and returned on 28.07.15 after that it went out again on 28.07.15. So it's not in my office
Now I want a list with every Item which is in my office and a list with items which are not in my office. I have to use Microsoft Access.
result 1: Items in my office
ID | Date_out | Date_in
---+----------+----------
2 | 01.01.15 | 13.05.15
result 2: Items outside my office
ID | Date_out | Date_in
---+----------+----------
1 | 01.01.15 |
4 | 28.07.15 |
The problem is that I have the items multiple times. When I group by ID the empty dates disappear like and I still have multiple items:
SELECT
table.item_ID,
table.Date_out,
table.Date_in
FROM table
WHERE table.date_in<Now()
GROUP BY table.item_ID
Does anyone have an idea?

another solution for your problem
SELECT
id,
CASE
WHEN
SUM(CASE WHEN date_in IS NULL THEN 1 ELSE 0 END) > 0
THEN 'out'
ELSE 'in'
END
FROM
table_name
GROUP BY id;

DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(ID INT NOT NULL
,Date_out DATE NOT NULL
,Date_in DATE NULL
,PRIMARY KEY(id,date_out)
);
INSERT INTO my_table VALUES
(1 ,'2015-01-01',NULL),
(2 ,'2015-01-01','2015-05-13'),
(2 ,'2014-07-16','2015-01-01'),
(4 ,'2015-07-28',NULL),
(4 ,'2014-07-16','2015-07-28');
SELECT x.id on_loan
FROM my_table x
JOIN
( SELECT id, MAX(date_out) max_date_out FROM my_table GROUP BY id) y
ON y.id = x.id
AND y.max_date_out = x.date_out
WHERE date_in IS NULL;
+---------+
| on_loan |
+---------+
| 1 |
| 4 |
+---------+
SELECT x.id in_stock
FROM my_table x
JOIN (SELECT id, MAX(date_out) max_date_out FROM my_table GROUP BY id) y
ON y.id = x.id
AND y.max_date_out = x.date_out
WHERE date_in IS NOT NULL;
+----------+
| in_stock |
+----------+
| 2 |
+----------+
or, less useful...
SELECT GROUP_CONCAT(CASE WHEN date_in IS NULL THEN x.id END) on_loan
, GROUP_CONCAT(CASE WHEN date_in IS NOT NULL THEN x.id END) in_stock
FROM my_table x
JOIN
( SELECT id, MAX(date_out) max_date_out FROM my_table GROUP BY id) y
ON y.id = x.id AND y.max_date_out = x.date_out;
+---------+----------+
| on_loan | in_stock |
+---------+----------+
| 1,4 | 2 |
+---------+----------+

Related

Finding created on dates for duplicates in SQL

I have one table of contact records and I'm trying to get the count of duplicate records that were created on each date. I'm not looking to include the original instance in the count. I'm using SQL Server.
Here's an example table
| email | created_on |
| ------------- | ---------- |
| aaa#email.com | 08-16-22 |
| bbb#email.com | 08-16-22 |
| zzz#email.com | 08-16-22 |
| bbb#email.com | 07-12-22 |
| aaa#email.com | 07-12-22 |
| zzz#email.com | 06-08-22 |
| aaa#email.com | 06-08-22 |
| bbb#email.com | 04-21-22 |
And I'm expecting to return
| created_on | dupe_count |
| ---------- | ---------- |
| 08-16-22 | 3 |
| 07-12-22 | 2 |
| 06-08-22 | 0 |
| 04-21-22 | 0 |
Edited to add error message:
error message
I created a sub table based on email and created date row number. Then, you query that, and ignore the date when the email first was created (row number 1). Works perfectly fine in this case.
Entire code:
Create table #Temp
(
email varchar(50),
dateCreated date
)
insert into #Temp
(email, dateCreated) values
('aaa#email.com', '08-16-22'),
('bbb#email.com', '08-16-22'),
('zzz#email.com', '08-16-22'),
('bbb#email.com', '07-12-22'),
('aaa#email.com', '07-12-22'),
('zzz#email.com', '06-08-22'),
('aaa#email.com', '06-08-22'),
('bbb#email.com', '04-21-22')
select datecreated, sum(case when r = 1 then 0 else 1 end) as duplicates
from
(
Select email, datecreated, ROW_NUMBER() over(partition by email
order by datecreated) as r from #Temp
) b
group by dateCreated
drop table #Temp
Output:
datecreated duplicates
2022-04-21 0
2022-06-08 0
2022-07-12 2
2022-08-16 3
You can calculate the difference between total count of emails for every day and the count of unique emails for the day:
select created_on,
count(email) - count(distinct email) as dupe_count
from cte
group by created_on
It seems I have misunderstood your request, and you wanted to consider previous created_on dates' too:
ct as (
select created_on,
(select case when (select count(*)
from cte t2
where t1.email = t2.email and t1.created_on > t2.created_on
) > 0 then email end) as c
from cte t1)
select created_on,
count(distinct c) as dupe_count
from ct
group by created_on
order by 1
It seems that in oracle it is also possible to aggregate it using one query:
select created_on,
count(distinct case when (select count(*)
from cte t2
where t1.email = t2.email and t1.created_on > t2.created_on
) > 0 then email end) as c
from cte t1
group by created_on
order by 1

SQL: How can I pick a cell value from one table as a condition to select another table

Hi I'm a new learner of SQL. How can I realize this process in SQL or perhaps with python if needed:
First, from table1, I randomly selected two results:
SELECT TOP 2 id, date
FROM table 1
WHERE date >= 2 AND date <= 6
ORDER BY RAND(CHECKSUM(*) * RAND())
+-----------+
| table1 |
+-----------+
| id | date |
+----+------+
| x | 3 |
| y | 4 |
+----+------+
I need to use the value x and y as conditions to display another table. For instance, using x, I can:
SELECT id, date
FROM table1
WHERE date >= 2 AND date <= 6 AND id = 'x'
ORDER BY date ASC
+-----------+
| table2 |
+-----------+
| id | date |
+----+------+
| x | 3 |
| x | 4 |
| x | 5 |
| x | 6 |
| x | 6 |
+----+------+
What I need is to get the length of table2 without duplication on date. For instance, table2 has 5 rows, but last two duplicate in date. So the final answer is 4 rows.
For id = y, I have to do the same thing (say table3) and compare the length of table3 and table2 to see if consistent.
If yes, then return the length (say, 4 rows); If no, then go back to table1 and select another two id (say, z and y).
I was thinking to use python to select value or create variables, then use python variables in SQL. But it is too much for a new learner. I really appreciate it if someone could help me out this process.
You can use subqueries with IN clause
Here is too a Version with two diemsnions, maybe this will help also
CREATE TABLE table1 ([id] varchar(2),[date] int)
GO
✓
SELECT id, date FROM table1
where date >= 2 and date <= 6
and id IN (
SELECT TOP 2 id FROM table1
WHERE date >= 2 and date <= 6
ORDER BY RAND(CHECKSUM(*) * RAND())
)
ORDER BY date ASC
GO
id | date
:- | ---:
SELECT id, date FROM table1
WHERE EXISTS (SELECT 1
FROM (
SELECT TOP 2 id,[date] FROM table1
WHERE date >= 2 and date <= 6
ORDER BY RAND(CHECKSUM(*) * RAND())) AS table2
WHERE table1.[id] = table2.[id]
AND table1.[date] = table2.[date])
GO
id | date
:- | ---:
db<>fiddle here

Select last changed row in sub-query

I have a table product:
id | owner_id | last_activity | box_id
------------------------------------
1 | 2 | 12/19/2014 | null
2 | 2 | 12/13/2014 | null
3 | 2 | 08/11/2014 | null
4 | 2 | 12/11/2014 | 99
5 | 2 | null | 99
6 | 2 | 12/15/2014 | 99
7 | 2 | null | 105
8 | 2 | null | 105
9 | 2 | null | 105
The only variable that I have is owner_id.
I need to select all products of a user, but if the product is in a box then only latest one should be selected.
Sample output for owner = 2 is following:
id | owner_id | last_activity | box_id
------------------------------------
1 | 2 | 12/19/2014 | null
2 | 2 | 12/13/2014 | null
3 | 2 | 08/11/2014 | null
6 | 2 | 12/15/2014 | 99
7 | 2 | null | 105
I'm not able to find a way to select the latest product from a box.
My current query, which does not return correct value, but can be executed:
SELECT p.* FROM product p
WHERE p.owner_id = 2
AND (
p.box IS NULL
OR (
p.box IS NOT NULL
AND
p.id = ( SELECT MAX(pp.id) FROM product pp
WHERE pp.box_id = p.box_id )
)
I tried with dates:
SELECT p.* FROM product p
WHERE p.owner_id = 2
AND (
p.box IS NULL
OR (
p.box IS NOT NULL
AND
p.id = ( SELECT * FROM (
SELECT pp.id FROM product pp
WHERE pp.box_id = p.box_id
ORDER BY last_activity desc
) WHERE rownum = 1
)
)
Which gives error: p.box_id is undefined as it's inside 2nd subquery.
Do you have any ideas how can I solve it?
The ROW_NUMBER analytical function might help with such queries:
SELECT "owner_id", "id", "box_id", "last_activity" FROM
(
SELECT "owner_id", "id", "box_id", "last_activity",
ROW_NUMBER()
OVER (PARTITION BY "box_id" ORDER BY "last_activity" DESC NULLS LAST) rn
-- ^^^^^^^^^^^^^^^
-- descending order, reject nulls after not null values
-- (this is the default, but making it
-- explicit here for self-documentation
-- purpose)
FROM T
WHERE "owner_id" = 2
) V
WHERE rn = 1 or "box_id" IS NULL
ORDER BY "id" -- <-- probably not necessary, but matches your example
See http://sqlfiddle.com/#!4/db775/8
there can be nulls as a value. If there are nulls in all products inside a box, then MIN(id) should be returned
Even if is is probably not a good idea to rely on id to order things is you think you need that, you will have to change the ORDER BY clause to:
... ORDER BY "last_activity" DESC NULLS LAST, "id" DESC
-- ^^^^^^^^^^^
Use exists
SELECT
p.*
FROM
product p
WHERE
p.owner_id = 2 AND
( p.box IS NULL OR
(
p.box IS NOT NULL AND
NOT EXISTS
(
SELECT
pp.id
FROM
product pp
WHERE
pp.box_id = p.box_id AND
pp.last_activity > p.last_activity
)
)
)
You can use union to first get all rows where box_is null and than fetch rows with max id and date where box_id is not null:
SELECT * FROM
(
SELECT id,owner_id,last_activity,box_id FROM product WHERE owner_id = 2 AND box_id IS NULL
UNION
SELECT MAX(id),owner_id,MAX(last_activity),box_id FROM product WHERE owner_id = 2 AND box_id IS NOT NULL GROUP BY owner_id, box_id
) T1
ORDER BY
id

divide a column into two based on another column value - ORACLE

First, hope the title expresses the issue. Otherwise, any suggest is welcomed. My issue is I have the following table structure:
+----+------+------------------+-------------+
| ID | Name | recipient_sender | user |
+----+------+------------------+-------------+
| 1 | A | 1 | X |
| 2 | B | 2 | Y |
| 3 | A | 2 | Z |
| 4 | B | 1 | U |
| | | | |
+----+------+------------------+-------------+
Whereby in the column recipient_sender the value 1 means the user is recipient, the value 2 means the user is sender.
I need to present data in the following way:
+----+------+-----------+---------+
| ID | Name | recipient | sender |
+----+------+-----------+---------+
| 1 | A | X | Z |
| 2 | B | U | Y |
+----+------+-----------+---------+
I've tried self-join but it did not work. I cannot use MAX with CASE WHEN, as the number of records is too big.
Note: Please ignore the bad table design as it's just a simplified example of the real one
Please try:
SELECT
MIN(ID) ID
Name,
max(case when recipient_sender=1 then user else null end) sender,
max(case when recipient_sender=2 then user else null end) recipient
From yourTable
group by Name
maybe you can try this:
select min(id) id,
name,
max(decode(recipient_sender, 1, user, '')) sender,
max(decode(recipient_sender, 2, user, '')) recipient
from t
group by name
You can check a demo here on SQLFiddle.
You can select values with this query
SELECT t.id,
t.name,
case
when t.recipient_sender = 1 then
t.user
ELSE
t2.user
END as recipient,
case
when t.recipient_sender = 2 then
t.user
ELSE
t2.user
END as sender
FROM your_table t
JOIN your_table t2
ON t.name = t2.name
AND t.id != t2.id
after this query you can add DISTINCT keyword or GROUP them ...
this query is used to join tables with column NAME but if you have some identity for message , join tables using that ,
Create new Table (with better struct):
insert into <newtable> as
select distinct
id,
name,
user as recipient,
(select user from <tablename> where id = recip.id and name = recip.name) as sender
from <tablename> recip
sorry, have no oracle here.

Mysql4: SQL for selecting one or zero record

Table layout:
CREATE TABLE t_order (id INT, custId INT, order DATE)
I'm looking for a SQL command to select a maximum of one row per order (the customer who owns the order is identified by a field named custId).
I want to select ONE of the customer's orders (doesn't matter which one, say sorted by id) if there is no order date given for any of the rows.
I want to retrieve an empty Resultset for the customerId, if there is already a record with given order date.
Here is an example. Per customer there should be one order at most (one without a date given). Orders that have already a date value should not appear at all.
+---------------------------------------------------------+
|id | custId | date |
+---------------------------------------------------------+
| 1 10 NULL |
| 2 11 2008-11-11 |
| 3 12 2008-10-23 |
| 4 11 NULL |
| 5 13 NULL |
| 6 13 NULL |
+---------------------------------------------------------+
|
|
| Result
\ | /
\ /
+---------------------------------------------------------+
|id | custId | date |
+---------------------------------------------------------+
| 1 10 NULL |
| |
| |
| |
| 5 13 NULL |
| |
+---------------------------------------------------------+
powered be JavE
Edit:
I've choosen glavić's answer as the correct one, because it provides
the correct result with slightly modified data:
+---------------------------------------------------------+
|id | custId | date |
+---------------------------------------------------------+
| 1 10 NULL |
| 2 11 2008-11-11 |
| 3 12 2008-10-23 |
| 4 11 NULL |
| 5 13 NULL |
| 6 13 NULL |
| 7 11 NULL |
+---------------------------------------------------------+
Sfossen's answer will not work when customers appear more than twice because of its where clause constraint a.id != b.id.
Quassnoi's answer does not work for me, as I run server version 4.0.24 which yields the following error:
alt text http://img25.imageshack.us/img25/8186/picture1vyj.png
For a specific customer it's:
SELECT *
FROM t_order
WHERE date IS NULL AND custId=? LIMIT 1
For all customers its:
SELECT a.*
FROM t_order a
LEFT JOIN t_order b ON a.custId=b.custID and a.id != b.id
WHERE a.date IS NULL AND b.date IS NULL
GROUP BY custId;
Try this:
SELECT to1.*
FROM t_order AS to1
WHERE
to1.date IS NULL AND
to1.custId NOT IN (
SELECT to2.custId
FROM t_order AS to2
WHERE to2.date IS NOT NULL
GROUP BY to2.custId
)
GROUP BY to1.custId
For MySQL 4:
SELECT to1.*
FROM t_order AS to1
LEFT JOIN t_order AS to2 ON
to2.custId = to1.custId AND
to2.date IS NOT NULL
WHERE
to1.date IS NULL AND
to2.id IS NULL
GROUP BY to1.custId
This query will use one pass over index on custId.
For each distinct custId it will use one subquery over same index.
No GROUP BY, no TEMPORARY and no FILESORT — efficient, if your table is large.
SELECT VERSION()
--------
'4.1.22-standard'
CREATE INDEX ix_order_cust_id ON t_order(custId)
SELECT id, custId, order_date
FROM (
SELECT o.*,
CASE
WHEN custId <> #c THEN
(
SELECT 1
FROM t_order oi
WHERE oi.custId = o.custId
AND order_date IS NOT NULL
LIMIT 1
)
END AS n,
#c <> custId AS f,
#c := custId
FROM
(
SELECT #c := -1
) r,
t_order o
ORDER BY custId
) oo
WHERE n IS NULL AND f
---------
1, 10, ''
5, 13, ''
First filter out rows with dates, then filter out any row that has a similar row with a lower id. This should work because the matching record with the least id is unique if id is unique.
select * from t_order o1
where date is null
and not exists (select * from t_order o2
where o2.date is null
and o1.custId = o2.custId
and o1.id > o2.id)