SQL join combine with condition - sql

Please how to achieve following result from this data?
(I am trying to combine join with conditions but still with some unwanted rows.)
Input is in one table only:
ID IN OUT
-----------------
1 6:00 null
2 7:11 null
2 null 16:30
3 null 19:00
Output (a view) should combine same IDs so the result will looks like:
ID IN OUT
------------------
1 6:00 null
2 7:11 16:30
3 null 19:00

You can try to achieve this with a CTE as follows:
CREATE TABLE #data
(
Id int not null,
inTime varchar(10) null,
outTime varchar(10) null
)
INSERT INTO #data VALUES
(1, '6:00', null),
(2 , '7:11', null),
(2 , null, '16:30'),
(3 , null, '19:00')
WITH combinedTable AS (
SELECT
Id,
MAX(inTime) AS A,
MAX(outTime) AS B
FROM #data
GROUP BY Id
HAVING MAX(2) = MIN(2)
AND MAX(3) = MIN(3)
)
SELECT *
FROM combinedTable
UNION ALL
SELECT *
FROM #data
WHERE ID NOT IN (SELECT ID FROM combinedTable)

The following can serve the purpose:
1. Have a table with ID and IN where IN is not equal to null:
(select ID, IN from table where In ne NULL()) as tableA
O/P would be:
ID IN
1 6:00
2 7:11
2.Have a table with ID and OUT where OUT is not equal to null:
(select ID, OUT from table where OUT ne NULL()) as tableB
O/P would be:
ID OUT
2 16:30
3 19:00
3.Outer Join the two tables:
select tableA.ID,tableA.IN, tableB.OUT
from table as tableA
outer full join
select ID,OUT from table as tableB
On tableA.ID=tableB.ID and tableA.IN ne Null() and tableB.OUT ne NULL()
Output will be the desired output:
ID IN OUT
1 6:00 null
2 7:11 16:30
3 null 19:00

Related

SQL View to flatten out a hierarchy

I have a table with some parent child relationships. I want to create a view that has all possible ids for a location id.
I need the sql to do this
Table:
ID PARENT_ID LOCATION_ID
1 NULL ABC
2 1 XYZ
3 NULL EFG
view results:
LOCATION_ID ID
XYZ 1
XYZ 2
ABC 1
ABC 2
EFG 3
You don't mention the database you are using, so I'll assume PostgreSQL. You can adjust the answer to your specific engine:
with recursive
n as (
select id, id as grp, location_id from t where parent_id is null
union all
select t.id, n.grp, t.location_id
from n
join t on t.parent_id = n.id
)
select b.id, a.location_id
from n a
join n b on a.grp = b.grp
Result:
id location_id
-- -----------
1 ABC
2 ABC
1 XYZ
2 XYZ
3 EFG
For the record, the data script I used is:
create table t (
id int,
parent_id int,
location_id varchar(10)
);
insert into t (id, parent_id, location_id) values
(1, null, 'ABC'),
(2, 1, 'XYZ'),
(3, null, 'EFG');

Complex Joining multiple tables in SQL Server for a fact table

Hi I have 4 dimensions and I am trying to insert multiple data from the dimensions into the fact table.
I have a Gunsales table that contains the majority of the data for the fact table and then primary keys from other tables that I would like to join on. homicide_id from the Homicide table, article_id from the BBC table, incident_id from the Gun_violence table and shooting_id from the School_shooting table. The rest of the data if from the Gunsales table.
INSERT INTO [dbo].[FactGunSales]
(
sale_id,
sale_date,
sale_state,
permit,
hand_gun,
long_gun,
other_gun,
multiple_gun,
incident_id,
homicide_id,
article_id,
shooitng_id)
So logically it is a full join of the Gun_sales table and an inner join for the ID keys in the other table but I am struggling to get this to work.
adding in the DDL for all the tables:
USE [Gun Violence]
GO
DROP TABLE IF EXISTS Gun_Violence
CREATE TABLE Gun_Violence(
incident_id int PRIMARY KEY,
incident_date date,
state_name varchar (50),
city_name varchar(50),
death int ,
injury int ,
)
DROP TABLE IF EXISTS Gun_Sales
CREATE TABLE Gun_Sales(
sale_id int PRIMARY KEY,
sale_date date,
sale_state varchar(50),
permit int,
hand_gun int ,
long_gun int ,
other_gun int ,
multiple_gun int ,
)
DROP TABLE IF EXISTS School_Shootings
CREATE TABLE School_Shootings(
shooting_id int PRIMARY KEY,
shooting_date date,
shooting_state varchar(50),
shooting_city varchar(50),
shooting_death int ,
shooting_injury int,
)
DROP TABLE IF EXISTS Homicide
CREATE TABLE Homicide(
Homicide_id int PRIMARY KEY,
homicide_state varchar(50),
homicide_victims int,
homeicide_date date
)
DROP TABLE IF EXISTS BBC
CREATE TABLE BBC(
ariticle_id int PRIMARY KEY,
article_date date,
article_link varchar(1000),
article_headline varchar(1000),
article_count int,
article_keyword varchar(100),
article_month varchar(10),
article_year int,
article_state varchar(50)
)
Output:
As mentioned above I am trying to create a new fact table that has all columns from the Gun_sales table and the primary keys from the other tables.
Thanks in advance
#RitaMurran Is't true? Do you want Something like this?
USE [Gun Violence]
go
INSERT INTO [dbo].[FACTGUNSALES]
([sale_id],
[sale_date],
[sale_state],
[hand_gun],
[other_gun],
[multiple_gun],
[incident_id],
[homicide_id],
[article_id],
[shooitng_id])
SELECT [sale_id],
[sale_date],
[sale_state],
[hand_gun],
[other_gun],
[multiple_gun],
[dbo].[GUN_VIOLENCE].incident_id,
[dbo].[HOMICIDE].homicide_id,
[dbo].[BBC].ariticle_id,
[dbo].[SCHOOL_SHOOTINGS].shooting_id
FROM [dbo].[GUN_SALES]
LEFT JOIN [dbo].[GUN_VIOLENCE]
ON ( [dbo].[GUN_VIOLENCE].incident_date = [dbo].[GUN_SALES].[sale_date] AND
[dbo].[GUN_VIOLENCE].state_name = [dbo].[GUN_SALES].[sale_state] )
LEFT JOIN [dbo].[SCHOOL_SHOOTINGS]
ON ( [dbo].[SCHOOL_SHOOTINGS].shooting_date = [dbo].[GUN_SALES].[sale_date] AND
[dbo].[SCHOOL_SHOOTINGS].shooting_state = [dbo].[GUN_SALES]. [sale_state] )
LEFT JOIN [dbo].[HOMICIDE]
ON ( [dbo].[HOMICIDE].homeicide_date = [dbo].[GUN_SALES].[sale_date] AND
[dbo].[HOMICIDE].homicide_state = [dbo].[GUN_SALES].[sale_state] )
LEFT JOIN [dbo].[BBC]
ON ( [dbo].[BBC].article_date = [dbo].[GUN_SALES].[sale_date] AND
[dbo].[BBC].[article_state] = [dbo].[Gun_Sales].[sale_state] )
EDIT:
#RitaMurran, I answer your question:
"Is there a way to have only distinct values when the join happens?"
with the example:
DECLARE #tbl_A TABLE (ID_A INT, col_A varChar(10))
INSERT #tbl_A (ID_A,col_A)
SELECT 1 AS ID_A,'aaa' AS col_A UNION ALL
SELECT 2 ,'bbb' UNION ALL
SELECT 3 ,'ccc' UNION ALL
SELECT 4 ,'ddd' UNION ALL
SELECT 5 ,'eee'
DECLARE #tbl_B TABLE (ID_B INT,ID_A_FK INT, col_B varChar(10))
INSERT #tbl_B (ID_B,ID_A_FK,col_B)
SELECT 1 AS ID_B,1 AS ID_A_FK,NULL AS col_B UNION ALL
SELECT 2 ,1 ,NULL UNION ALL
SELECT 3 ,3 ,NULL UNION ALL
SELECT 4 ,3 ,NULL UNION ALL
SELECT 5 ,3 ,NULL
SELECT *
FROM #tbl_A tbA
LEFT JOIN
( select ID_A_FK,col_B FROM #tbl_B GROUP BY ID_A_FK,col_B ) tbB ON tbA.ID_A = tbB.ID_A_FK
The Output:
ID_A col_A ID_A_FK col_B
1 aaa 1 NULL
2 bbb NULL NULL
3 ccc 3 NULL
4 ddd NULL NULL
5 eee NULL NULL
If col_B has value,The output is like this:
INSERT #tbl_B (ID_B,ID_A_FK,col_B)
SELECT 1 AS ID_B,1 AS ID_A_FK,'a1' AS col_B UNION ALL
SELECT 2 ,1 ,'a2' UNION ALL
SELECT 3 ,3 ,'c1' UNION ALL
SELECT 4 ,3 ,'c2' UNION ALL
SELECT 5 ,3 ,'c3'
Output:
ID_A col_A ID_A_FK col_B
1 aaa 1 a1
1 aaa 1 a2
2 bbb NULL NULL
3 ccc 3 c1
3 ccc 3 c2
3 ccc 3 c3
4 ddd NULL NULL
5 eee NULL NULL

Return results from a table match on exact number of rows

I have two tables A and B, that are in a many to many relationship in a third table. What A want to achieve is get the "repeating" A rows based on B. For example:
table A table B table A_B
---------- ---------- ----------
1 A 1 A
2 B 1 B
3 C 2 A
4 D 2 B
5 3 A
3 B
3 C
4 A
4 D
5 A
What I want is, when searching table A_B by lets say '1', to get only 2, although 3 has both A and B and 4 has A, same goes for 5 too, it matches A but only A so it should be ignored as well. I've tried some suggestions form similar questions with cross join but I had no luck. I am trying to achieve this with just selects and joins, without stored procedures or temporary tables. Any suggestions is welcomed, thank you.
Repeat all base table rows for EACH left join row match
I want my output to look like:
table A_B
----------
2 A
2 B
Or if possible it would be even better if it matches the A_id by which the search is being done
table A_B
----------
1 A
1 B
2 A
2 B
However, the B_id column is not as important so if it is only
table A_B
----------
2
or
table A_B
----------
1
2
is acceptable as well.
EDIT 1:
Until now this is what I've came up with, although a bit unclean but it gets the expected result
select
A_id
from
tableA_B
where
A_id in
(
select
A_id
from
tableA_B
group by
A_id
having
count (A_id) IN (
select
count (A_id)
from
tableA_B
where
A_id = 1
)
)
AND
B_id IN (
select
B_id
from
tableA_B
where
A_id = 1
)
group by
A_id
Basically process of elimination, step by step. It would be ideal if it took only one step.
EDIT 2:
I'm sorry I left out some important information, my B values can be repeated for instance
table A table B table A_B
---------- ---------- ----------
1 A 1 A
2 B 1 B
3 c 2 A
4 D 2 B
5 AB 3 A
6 3 B
3 C
4 A
4 D
5 A
6 AB
so using XML path may return incorrect values. Because in my case it will return 6 as well which is incorrect. I apologies for leaving out this information.
Other solution which use INTERSECT could be:
CREATE TABLE tableA_B (A_id INT, B_id VARCHAR(8))
GO
INSERT INTO tableA_B VALUES
(1,'A'),(1,'B'),(2,'A'),(2,'B'),(3,'A'),(3,'B'),(3,'C'),(4,'A'),(4,'D'),(5,'A')
GO
DECLARE #x INT = 1;
SELECT A_id FROM tableA_B ab1
LEFT JOIN (
SELECT B_id FROM tableA_B
WHERE A_id=#x
) ab2 ON ab1.B_id=ab2.B_id
GROUP BY ab1.A_id
HAVING COUNT(*)=(SELECT COUNT(*) FROM tableA_B WHERE A_id=#x)
INTERSECT
SELECT A_id FROM tableA_B ab1
JOIN (
SELECT B_id FROM tableA_B
WHERE A_id=#x
) ab2 ON ab1.B_id=ab2.B_id
GROUP BY ab1.A_id
HAVING COUNT(*)=(SELECT COUNT(*) FROM tableA_B WHERE A_id=#x)
DROP TABLE tableA_B
GO
Try this,
declare #A_B table(col int,col2 varchar(30))
insert into #A_B VALUES
(1 ,'A') ,(1 ,'B') ,(2 ,'A') ,(2 ,'B') ,(3 ,'A') ,(3 ,'B')
,(3 ,'C') ,(4 ,'A') ,(4 ,'D') ,(5 ,'A'),(6 ,'AB')
declare #i int=1
declare #007 char(1)='-'
;with CTE as
(
select col,col2
,(select #007+col2 from #A_B y
where col=x.col for xml path(''))ConcateCol
from #A_B x
--where col=#i
)
select col,col2
from cte c
where
exists(select * from cte c1
where col=#i and c.ConcateCol=c1.ConcateCol)
you can further maniplate to get whatever desire output
;With tableA(ID)
AS
(
Select 1 uNION ALL
Select 2 uNION ALL
Select 3 uNION ALL
Select 4
)
, tableB(VAL)
As
(
SELECT 'A' UNION ALL
SELECT 'B' UNION ALL
SELECT 'C' UNION ALL
SELECT 'D'
)
SELECT ID,VAL FROM
(
SELECT *,ROW_NUMBER()OVER(PARTITION BY ID ORDER BY ID)AS Seq FROM tableA
CROSS JOIN tableB
)Dt
WHERE ID In (SELECT Id From tableA where id in(1,2) ) AND Dt.Seq<3
OutPut
table A_B
----------
1 A
1 B
2 A
2 B

Sql checking if a date from 1 table is between 2 dates from different table

i have 2 sql tables
has Id, date, boolean value
has Id, date
so table 1 looks like this
1 3/1/2017 false
2 3/1/2017 true
1 1/1/2017 false
2 10/12/2016 false
table 2 like this
1 3/1/2017
2 3/1/2017
1 2/1/2017
1 12/12/2016
The result I want is for each pair of dates in table 1 that have the same id and are following each other for example for id 1 it is 1/1/2017 and 3/1/2017
to find if there is a date in table 2 with the same id that is between those dates (same day inclusive) and the boolean is false.
so for example the result in this case would be
for id1 2/1/2017, 3/1/2017
How can I do this?
How many rows there can be in Table1 with the same ID? I assume 2, but the query will work reasonably even with 1 or 3+.
"are following each other" - any two dates will follow each other, unless they are the same, so the only real check below is for inequality.
"the boolean is false" - there are two rows, which can have different boolean values. I assume both of them have to be false. (false is 0, true is 1)
Sample data
This is how your sample data should be presented in your question. At least you should write dates in a way that we don't have to guess what is month and what is day.
DECLARE #Table1 TABLE (ID int, dt date, Flag bit);
INSERT INTO #Table1 (ID, dt, Flag) VALUES
(1, '2017-01-03', 'false'),
(2, '2017-01-03', 'true'),
(1, '2017-01-01', 'false'),
(2, '2016-12-10', 'false');
DECLARE #Table2 TABLE (ID int, dt date);
INSERT INTO #Table2 (ID, dt) VALUES
(1, '2017-01-03'),
(2, '2017-01-03'),
(1, '2017-01-02'),
(1, '2016-12-12');
Query
WITH
CTE
AS
(
SELECT
ID
,MIN(dt) AS StartDT
,MAX(dt) AS EndDT
,MAX(CAST(Flag AS int)) AS MaxFlag
FROM #Table1 AS Table1
GROUP BY ID
)
SELECT
CTE.ID
,A.dt
FROM
CTE
CROSS APPLY
(
SELECT
Table2.dt
FROM #Table2 AS Table2
WHERE
Table2.ID = CTE.ID
AND Table2.dt >= CTE.StartDT
AND Table2.dt <= CTE.EndDT
) AS A
WHERE
StartDT < EndDT -- "are following each other"
AND MaxFlag = 0 -- "the boolean is false" for both IDs
;
Result
+----+------------+
| ID | dt |
+----+------------+
| 1 | 2017-01-02 |
| 1 | 2017-01-03 |
+----+------------+
Index on Table2 on (ID, dt) will help a lot.

update oldID field based on fields in the same table

I need help with the following query.
create table #table1
(id int not null primary key identity,
customer_name varchar(25),
usage float,
oldID int null
)
insert into #table1 values('ABC',46.5,null)
insert into #table1 values('ABC',46.5,null)
insert into #table1 values('DEF',36.8,null)
insert into #table1 values('XYZ',50.1,null)
insert into #table1 values('DEF',36.8,null)
insert into #table1 values('XYZ',50.1,null)
select * from #table1
I want my table to be updated like this
id customer_name usage oldID
----------- ------------------------- ---------------------- -----------
1 ABC 46.5 NULL
2 ABC 46.5 1
3 DEF 36.8 NULL
4 XYZ 50.1 NULL
5 DEF 36.8 3
6 XYZ 50.1 4
The two records with the same name and usage means the later record was renewed.
In the new record the oldID field should point to its old record (ID).
Although in my actual table, I have a bunch of date fields which I probably can use but this would help me for now.
Try this using a CTE:
;WITH data AS
(
SELECT
id, customer_name,
OldID = (SELECT MIN(id) FROM #table1 t2 WHERE t2.customer_name = t.customer_name)
FROM #table1 t
)
UPDATE #table1
SET OldID = data.OldID
FROM Data
WHERE
data.customer_Name = #table1.customer_name
AND #table1.ID <> data.oldid
select * from #table1
The Data CTE basically just determines the minimum ID for each customer, and if that customer's ID isn't that minimum ID, then OldID is set to that ID value.
When I run this, I get a resulting output:
id customer_name usage oldID
1 ABC 46.5 NULL
2 ABC 46.5 1
3 DEF 36.8 NULL
4 XYZ 50.1 NULL
5 DEF 36.8 3
6 XYZ 50.1 4
With cte, without subquerys, updating only customers with several rows:
with cte as (
select customer_name, min( id ) as id
from #table1
group by customer_name
having count(*) > 1
)
update #table1
set oldID = cte.id
from cte
where #table1.customer_name = cte.customer_name
and #table1.id != cte.id