SQL: LEFT JOIN for each value - sql

I'm a begginer with SQL and got a small problem. Let's assume I got a table looking like:
ID Month
1 Jan
2 Feb
2 June
3 Dec
Now I want to have that every ID-Value got every month, ie
ID Month
1 Jan
1 Feb
. .
.
.
2 Jan
2 Feb
.
.
and so on.
I tried to create another table including all months and the use of the command "Left Join". But this only includes all months once for the whole table, but not for every ID seperately.
I tried:
Create Table merged as
select ID, Month
from Data
Left outer join months On data.month=months.month;

You would typically do this with a cross join. If all months are in the table, then you can do:
select i.id, m.month
from (select distinct id from t) i cross join
(select distinct month from t) m;
You may have another source for the lists of ids and months.
EDIT:
Michael, normally when you are asking a new question, you should do it as another question. This question clearly does not have an outcome column; changing the question would invalidate this answer and hence draw downvotes -- so that is rude.
But, this is an easy change to the query:
select i.id, m.month, t.outcome
from (select distinct id from t) i cross join
(select distinct month from t) m left join
t
on t.id = i.id and t.month = m.month;

SELECT T1.ID,A._Month FROM #Table T1 CROSS JOIN (SELECT ID,_Month FROM #Table) A ORDER BY ID

Related

look up dynamic value from range in another table

I have 2 tables. The first one is a detail table with years and actual limit values. The second table has max control limits but only for certain years.
Table 1 & 2
What I want to do is list all of the detail records but pull in the values of the control limits if the year is less than the next one listed in the control table.
Desired results:
results
I have tried this query but it duplicates 2015, 2016 and 2017.
SELECT d.id, d.yeard, d.value, c.Column1
FROM detailTbl d
RIGHT OUTER JOIN controlTbl c ON d.dated <= c.datec
You may use Row_Number() function as the following to remove duplicates:
with cte as
(
Select D.id,D.yeard,D.val, C.limitVal,
row_number() over (partition by D.id order by C.yeard desc) as rn from
detailTbl D left join controlTbl C
on D.yeard>=C.yeard
)
Select B.id,B.yeard,B.val,B.limitVal from
cte B where B.rn=1 order by B.id
See a demo on MySQL 8.0 from here.

LEFT JOIN by closer value condition

I have this query
SELECT
loc.proceso,
loc.codigo_municipio,
loc.codigo_concejo,
loc.concejo,
(CASE
WHEN loc.poblacion IS NOT NULL THEN loc.poblacion
ELSE pob.valor
END) AS poblacion
FROM develop.031401_elecciones_dimension_localizacion_electoral AS loc
LEFT JOIN develop.031401_elecciones_dimension_proceso_electoral AS proc
ON loc.proceso = proc.proceso
LEFT JOIN develop.020101_t05 AS pob
ON loc.codigo_municipio = CAST(pob.cmun AS INT) AND pob.year = proc.anno_eleccion
In the second LEFT JOIN, I would like to change the second condition pob.year = proc.anno_eleccion so that it does not only search for the exact year when joining. Instead, I would like to get the closer year stored in my pob table. For example, the first year stored in pob is 2003, so I want all the entries in loc whose year is lower than 2003 to be matched with that value when performing the join. Also at the inverse, the last year stored in pob is 2020, so I want those entries in loc whose year is 2021 (or even greater), to be matched with the 2020 row from my pob table. When the exact year is contained in pob table, it should be used for the join.
1. If you want the nearest year to NOW
I don't think of a direct join but you can try this one by using ROW_NUMBER() function to sort data by year and pick the first result to join:
(WHERE rn = 1 picks the first index, so it prevents any duplicate)
LEFT JOIN
(SELECT T.* FROM
(SELECT ROW_NUMBER() OVER (PARTITION BY pob.cmun ORDER BY pob.year DESC) AS rn,
*
FROM develop.020101_t05) AS T
WHERE rn = 1) AS pob
ON loc.codigo_municipio = CAST(pob.cmun AS INT) AND pob.year = proc.anno_eleccion
2. If you want the nearest year to your data
Even it's not best practice, you can join your data using comparison operators on join condition. Then, take the difference between two years, sort the difference ascending and pick the first result using ROW_NUMBER() function. See example:
SELECT * FROM (
SELECT ROW_NUMBER() OVER (PARTITION BY a.Id ORDER BY a.Year - b.Year) AS RowNumber,
a.Id,
a.Year,
b.Year,
a.Year - b.Year AS YearDiff
FROM a
LEFT JOIN b ON a.Id = b.Id AND a.Year >= b.Year) AS T
WHERE RowNumber = 1

Full outer self join with date column

I have a table contains weekly data, with a column to identify when the data was loaded in the week.
This is the sample data
====================
Load_Date |Code
====================
1 Oct 2018 |875465
8 Oct 2018 |875465
15 Oct 2018 |875465
Additionally, this table has data for all 52 weeks, so if I do a DISTINCT Load_Date, it gives me 52 rows with each week's date.
I am trying to join the table with itself such that I can identify the weeks where Code IS NULL.
The query that I tried forming is
SELECT * FROM
(--Query to get all distinct weeks
SELECT DISTINCT LOAD_DATE FROM ztable
) dates
FULL OUTER JOIN
(
SELECT DISTINCT CODE, LOAD_DATE
FROM ztable
) z
ON z.LOAD_DATE = dates.LOAD_DATE
I have tried using Left Join too but the number of records against each code is the number of rows that the code has in the table, not 52.
For example, if the code has 3 rows then I'll see only 3 rows with this join.
What am I missing?
I think you are trying to find code/load_date combinations that don't exist. Going by your description (not the sample code), I think this is:
select d.load_date, c.code
from (select distinct load_date from t) d cross join
(select distinct code from t) c left join
t
on d.load_date = t.load_date and c.code = t.code
where t.code is null;

SQL: Find all records in Table A that do not have a value in Table B based on a column value in Table B

Using Microsoft SQL Server 2012, I'm trying to get all businesses who did not make a payment in 2016.
Here is my schema
Business Table
---------------
Id Name
1 Business A
2 Business B
Payments Table
---------------
Id BusinessId Year
1 2 2016
2 1 2017
3 2 2017
I need a SQL statement that returns Business A since it does not have a payment for 2016.
Here is what I have tried:
SELECT [Business].Name
FROM Businesses as [Business]
Left Outer JOIN Payments as [Payment]
ON [Payment].BusinessId = [Business].Id
Where [Business].Id is null and [Payment].TaxYear = 2016
Any help would be greatly appreciated.
Also, the title might be a little convoluted, so a suggested edit for the title of this question would be welcome as well.
NOT EXISTS, should return Business A since it does not have a 2016 transaction
SELECT B.Name
FROM Business as B
WHERE NOT EXISTS (
SELECT 1 FROM Payments P
Where P.TaxYear = 2016
AND P.BusinessId = B.Id )
I think you are looking for the combination of businesses and years that are not in the payments table. If so:
select b.*
from businesses b cross join
(select distinct year from payments) y left join
payments p
on b.id = p.businessid and b.year = y.year
where p.businessid is null;
The cross join generates all combinations of businesses and years. The left join and where clause then finds the ones that don't exist.
If you are only looking for one year, then #maSTAShuFu answer is the correct approach.

SQL query for getting count on same table using left outer join

I have a table from which I need to get the count grouped on two columns.
The table has two columns one datetime column and another one is success value(-1,1,0)
What i am looking for is something like this:
Count of success value for each month:
month----success-----count
11------- -1 ------- 50
11------- 1 --------- 50
11------- 0 ------- 50
12------- -1 ------- 50
12------- 1 ------- 50
12------- 0 ------- 50
If there is no success value for a month then the count should be null or zero.
I have tried with left outer join as well but of no use it gives the count incorrectly.
You need to cross join all the available months, against the 3 success values to build a virtual matrix, which can then be left joined to the actual data
select m.month, s.success, COUNT(t.month)
from (select distinct MONTH from tbl) m
cross join (select -1 success union all select 1 union all select 0) s
left join tbl t on t.month = m.month and t.success = s.success
group by m.month, s.success
If you need missing months as well, that can be done, just slightly more complicated by changing the subquery "m" above.
#updated
Count(*) will always return at least 1 for left joins. count(colname) from the right part of the left join to be correct.
You probably need a table with the just the values from 1-12 to join with so you can get a zero count.