Self join with inner and outer join query - sql

I have a table that is set up so that one column (attribute) contains information like first name, last name, account number and any other information related to a thing in the database. Another column (attributeType) contains a number indicating what the attribute is e.g. 1 might be first name, 2 last name and 3 account number etc. There is another column (enddate) indicating if the record is current by having a date there. Usually it will be set to the year 9999 when current and some date in the past otherwise. All data describing the same thing has a unique value too in another column (entity) so that each record with the same number in the entity column will be describing the one person. E.g.
entity attribute attributetype enddate
------ --------- ------------- --------
1 ben 1 9999-1-1
1 alt 2 9999-1-1
1 12345 3 9999-1-1
2 sam 1 9999-1-1
2 smith 2 9999-1-1
2 98765 3 1981-1-1
I want to select a person from the above table with a specific 1st and last name where the name will be current but not output the account number if it is not. Assuming the table is called tblAccount I do the following for the name part:
select ta1.attribute '1st Name', ta2.attribute 'last name'
from tblAccount ta1
inner join tblAccount ta2 on ta1.entity = ta2.entity
where ta1.attribute = 'sam' and ta2.attribute = 'smith'
and ta1.attributetype = 1 and ta2. attributetype = 2
and ta1.enddate > getdate() and ta2.enddate > getdate()
and it outputs the first and last names as expected, but when I want to include the account number column I get nothing output:
select ta1.attribute '1st Name', ta2.attribute 'last name', ta3.attribute 'account#'
from tblAccount ta1
inner join tblAccount ta2 on ta1.entity = ta2.entity
left join tblAccount ta3 on ta1.entity = ta3.entity
where ta1.attribute = 'sam' and ta2.attribute = 'smith'
and ta1.attributetype = 1 and ta2. attributetype = 2
and ta1.enddate > getdate() and ta2.enddate > getdate()
and ta3.attributetype = 3 and ta3.enddate > getdate()
What I would like to see is the first and last names output with nothing in the account# column in the above case where it is not current. What am I doing wrong and how can I correct this query?

You have to move the date comparison to the join condition:
select ta1.attribute '1st Name'
, ta2.attribute 'last name'
, ta3.attribute 'account#'
from tblAccount ta1
inner join tblAccount ta2
on ta1.entity = ta2.entity
and ta1.attributetype = 1 and ta2. attributetype = 2
and ta1.enddate > getdate() and ta2.enddate > getdate()
left join tblAccount ta3 on ta1.entity = ta3.entity
and ta3.attributetype = 3 and ta3.enddate > getdate()
where ta1.attribute = 'sam' and ta2.attribute = 'smith'
When it's in the where clause it's comparing getdate() to NULL if there is no account, which returns NULL. So no record.
EDIT:
In response to the valid concern about multiple valid records, and to make the code a little more maintenance freindly:
DECLARE #FNAME VARCHAR(50) = 'sam'
, #LNAME VARCHAR(50) = 'smith'
, #now DATETIME2(7) = GETDATE();
SELECT
name.[1st Name]
, name.[last name]
, name.entity
,
(
select
top 1
ta3.attribute
FROM tblAccount ta3
WHERE
ta3.entity = name.entity
and
ta3.attributetype = 3
and
ta3.enddate > #now
ORDER BY
ta3.enddate
)
FROM
(
select
ta1.attribute '1st Name'
, ta2.attribute 'last name'
, ta.entity
, ROW_NUMBER()
OVER(
PARTITION BY
ta1.entity
ORDER BY
ta1.enddate
) r
from
tblAccount ta1
inner join tblAccount ta2
on
ta1.entity = ta2.entity
and
ta2. attributetype = 2
and
ta2.enddate > #now
and
ta2.attribute = #LNAME
where
ta1.attributetype = 1
and
ta1.attribute = #fname
and
ta1.enddate > #now
) name
WHERE
NAME.r = 1
;
This code works around the implied assumptions of one entity per first/last name and exactly one enddate after the execution time. The variables are a little more stored proc friendly, and allow you to change the "as of" date. And if you're stuck with EAV, you will likely want stored procs. I'm taking the first record ending after the date in question, on the assumption that any later record(s) should only be valid after that one expires. Maybe it's overkill, since it's beyond the scope of the OP's question but it's a valid point.
I say "stuck with EAV". While EAV isn't always bad; neither is shooting someone in the back. In either case, you'd better have solid justification if you expect to get it past a jury. In a NoSQL storage pattern it's fine, but EAV is usually a poor implementation pattern for the RDBMS paradigm.
Though from the OP's later comment, it looks like he's hit on one of the better reasons.

Each attribute is actually a distinct entity in this model, but they are all sharing the same storage in the same physical table (Why?). This yields:
with data as (
select entity = 1, attribute = 'ben', attributeType=1, enddate = convert(datetime,'99990101') union all
select entity = 1, attribute = 'alt', attributeType=2, enddate = convert(datetime,'99990101') union all
select entity = 1, attribute = '12345', attributeType=3, enddate = convert(datetime,'99990101') union all
select entity = 2, attribute = 'sam', attributeType=1, enddate = convert(datetime,'99990101') union all
select entity = 2, attribute = 'smith', attributeType=2, enddate = convert(datetime,'99990101') union all
select entity = 2, attribute = '67890', attributeType=3, enddate = convert(datetime,'99990101') union all
select entity = 2, attribute = '68790', attributeType=3, enddate = convert(datetime,'20130331') union all
select entity = 2, attribute = '876', attributeType=3, enddate = convert(datetime,'19810101')
)
select top 1
FirstName, LastName, AccountNum
from (
select top 1
a1.entity, FirstName, LastName
from (
select entity, enddate, attribute as FirstName
from data d
where d.enddate >= getdate()
and attributeType = 1
) a1
join (
select entity, enddate, attribute as LastName
from data
where enddate >= getdate()
and attributeType = 2
) a2 on a1.entity = a2.entity
and a1.enddate = a2.enddate
where FirstName = 'sam' and LastName = 'smith'
and a1.enddate >= getdate() and a2.enddate >= getdate()
order by a1.enddate
) E
left join (
select entity, enddate, attribute as AccountNum
from data
where enddate >= getdate()
and attributeType = 3
) a3 on a3.entity = E.entity
order by a3.enddate
returning:
FirstName LastName AccountNum
--------- -------- ----------
sam smith 68790
Note that it is quite common for accountign departments, at least, to enter future transactions during quiet times of the month, especially if those transactions will take effect during busy times of the month (ie month-end). The same for annual transactions. One should not assume that only one record can exist with an expiry > getdate().

Related

How to get the most recent record of multiple of the same records in a table while joining another table?

SELECT tblSign.sigdate,tblSign.sigtime,tblSign.sigact,tblSign.esignature,tblEmpl.fname,tblEmpl.lname,tblEmpl.location, tblEmpl.estatus,tblLocs.unit,tblLocs.descript,TblLocs.addr1,tblLocs.city,tblLocs.state, tblLocs.zip
FROM tblEmpl
LEFT JOIN tblSign
ON tblSign.eight_id = tblEmpl.eight_id
AND tblSign.formid = '9648'
AND tblSign.sigact <> 'O'
AND tblSign.sigdate >= '2022-11-01'
LEFT JOIN tblLocs
ON tblEmpl.location = tblLocs.location
WHERE tblEmpl.estatus = 'A'
AND tblEmpl.location = '013'
ORDER BY
tblSign.sigdate ASC;
My table Sign has multiple records with the same eight_id so Im just trying to join tables getting the most recent record from tblSign besides multiple records
Data I get
Sigdate
fname
lname
location
sigact
2022-11-01
Bill
Lee
023
A
2022-10-01
Bill
Lee
023
A
2022-11-01
Carter
Hill
555
A
This is what I want :
Sigdate
fname
lname
location
sigact
2022-11-01
Bill
Lee
023
A
2022-11-01
Carter
Hill
555
A
Start by getting into better code-writing habits. Having all column names in one long string is horrible for readability and consequently troubleshooting. You can select the most recent record from a table by using a ROW_NUMBER function. I took your code, cleaned it up, added a derived table and in the derived table added a ROW_NUMBER function. I can't validate that the query works because you didn't post example source data from your tblEmpl, tblSign, and tblLocs tables. I'm not sure if the AND tblSign.sigact <> 'O' is valid in the derived table because it's not clear if you were trying to just limit the date range or that was your attempt to retrieve the most recent date.
SELECT
tblSign.sigdate
, tblSign.sigtime
, tblSign.sigact
, tblSign.esignature
, tblEmpl.fname
, tblEmpl.lname
, tblEmpl.location
, tblEmpl.estatus,tblLocs.unit
, tblLocs.descript
, TblLocs.addr1
, tblLocs.city
, tblLocs.state
, tblLocs.zip
FROM tblEmpl
LEFT JOIN (
SELECT *
--Used to order the records for each eight_id by the date.
--Most recent date for each eight_id will have row_num = 1.
, ROW_NUMBER() OVER(PARTITION BY eight_id ORDER BY sigdat DESC) as row_num
FROM tblSign as ts
WHERE tblSign.formid = '9648'
AND tblSign.sigact <> 'O'
AND tblSign.sigdate >= '2022-11-01' --Not clear if this is just to limit results or an attempt to get most recent date in the failed original code.
) as ts
ON ts.eight_id = tblEmpl.eight_id
AND ts.row_num = 1 --Use to limit to most recent date.
LEFT JOIN tblLocs
ON tblEmpl.location = tblLocs.location
WHERE tblEmpl.estatus = 'A'
AND tblEmpl.location = '013'
ORDER BY
tblSign.sigdate ASC
You use ROW_NUMBER to get the last entry in my case for every esignature, as i thought this must be unique
WITH CTE AS
(SELECT
tblSign.sigdate,
tblSign.sigtime,
tblSign.sigact,
tblSign.esignature,
tblEmpl.fname,
tblEmpl.lname,
tblEmpl.location,
tblEmpl.estatus,
tblLocs.unit,
tblLocs.descript,
TblLocs.addr1,
tblLocs.city,
tblLocs.state,
tblLocs.zip,
ROW_NUMBER() OVER(PARTITION BY tblSign.esignature ORDER BY tblSign.sigdate DESC) rn
FROM
tblEmpl
LEFT JOIN
tblSign ON tblSign.eight_id = tblEmpl.eight_id
AND tblSign.formid = '9648'
AND tblSign.sigact <> 'O'
AND tblSign.sigdate >= '2022-11-01'
LEFT JOIN
tblLocs ON tblEmpl.location = tblLocs.location
WHERE
tblEmpl.estatus = 'A'
AND tblEmpl.location = '013')
SELECT sigdate,
sigtime,
sigact,
esignature,
fname,
lname,
location,
estatus,
unit,
descript,
addr1,
city,
state,
zip
WHERE rn = 1
ORDER BY sigdate ASC;

Select the first row that meets a condition + 1 row above

I have written a SQL query to return the following results:
Rows returned
However, I need to further 'filter' it down to only the rows highlighted in yellow, because I want the first row that has param_id = 'update_flag' AND param_val = '1'. I also need the row above that, because I need to retrieve the param_val value (202017) that corresponds to param_id = 'period_from' for the same Order Number (156 in this example).
This is my code, I'm getting data from 2 different tables:
SELECT
report1.orderno AS t1_orderno,
report2.orderno AS t2_orderno,
report1.report_name AS t1_reportname,
report2.report_name AS t2_reportname,
report1.variant,
report1.status,
report1.client,
report2.param_id,
report2.param_val
FROM report1
INNER JOIN
report2
ON report1.orderno = report2.orderno
AND report2.param_id IN
(
'period_from',
'update_flag'
)
AND report1.report_name = 'PR28'
AND report1.variant = '20'
AND report1.status = 'T'
AND report1.client = '10'
ORDER BY
report1.orderno DESC
Thanks for any help in advance, it's doing my head in!
WITH num_row AS (
SELECT row_number() OVER (ORDER BY report1.orderno DESC) as nom,
report1.orderno AS t1_orderno,
report2.orderno AS t2_orderno,
report1.report_name AS t1_reportname,
report2.report_name AS t2_reportname,
report1.variant,
report1.status,
report1.client,
report2.param_id,
report2.param_val
FROM report1
INNER JOIN
report2
ON report1.orderno = report2.orderno
AND report2.param_id IN (
'period_from',
'update_flag'
)
AND report1.report_name = 'PR28'
AND report1.variant = '20'
AND report1.status = 'T'
AND report1.client = '10'
)
SELECT * FROM num_row
WHERE nom BETWEEN 1 AND 2

3 Statements into a view

I have 3 SQL statements that I would like to create a view and return 3 columns, each representing a count.
Here are my statements
SELECT Count(*)
FROM PlaceEvents
WHERE PlaceID = {placeID} AND EndDateTimeUTC >= GETUTCDATE()
SELECT Count(*)
FROM PlaceAnnouncements
WHERE PlaceID = {placeID}
SELECT Count(*)
FROM PlaceFeedback
WHERE PlaceID = {placeID} AND IsSystem = 0
I know how to create a basic view but how do I create one that will let me have those 3 column place placeID as a column to use for filtering
I would like to do the following to return the proper data
SELECT *
FROM vMyCountView
WHERE PlaceID = 1
CREATE VIEW vMyCountView AS
(...) AS ActiveEvents,
(...) AS Announcements,
(...) AS UserFeedback,
PlaceID
I'd rather use a function then a view:
This allows you to pass in any parameters you like (I assumed placeId is an INT) and deal with it within your query. The handling is quite as easy as with a View:
CREATE FUNCTION MyCountFunction(#PlaceID INT)
RETURNS TABLE
AS
RETURN
SELECT
(SELECT Count(*) FROM PlaceEvents WHERE PlaceID = #PlaceID AND EndDateTimeUTC >= GETUTCDATE()) AS ActiveEvents
,(SELECT Count(*) FROM PlaceAnnouncements WHERE PlaceID = #PlaceID) AS Announcements
,(SELECT Count(*) FROM PlaceFeedback WHERE PlaceID = #PlaceID AND IsSystem = 0) AS UserFeedback
,#PlaceID AS PlaceID;
GO
And this is how you call it. You can use this for JOINs or with APPLY also...
SELECT * FROM dbo.MyCountFunction(3);
You can combine them as multiple select sub-queries.
CREATE VIEW vMyCountView AS
SELECT
(SELECT Count(*) FROM PlaceEvents
WHERE PlaceID = s.placeID AND EndDateTimeUTC >= GETUTCDATE()) AS ActiveEvents,
(SELECT Count(*) FROM PlaceAnnouncements
WHERE PlaceID = s.placeID) AS Announcements,
(SELECT Count(*) FROM PlaceFeedback
WHERE PlaceID = s.placeID AND IsSystem = 0) AS UserFeedback,
placeID
from Sometable s
By definition, view is a single select statement. You can use join, union and so on if it makes sense to your business logic provided create view is the only query in the batch.
You can make a view like that with GROUP BY:
SELECT
PlaceId
, Count(peId) AS ActiveEvents
, COUNT(paId) AS Announcements
, COUNT(fbId) AS UserFeedback
FROM (
SELECT PlaceId, 1 AS peId, NULL AS paId, NULL AS fbId
FROM PlaceEvents
WHERE EndDateTimeUTC >= GETUTCDATE()
UNION ALL
SELECT PlaceId, NULL AS peId, 1 AS paId, NULL AS fbId
FROM PlaceAnnouncements
UNION ALL
SELECT PlaceId, NULL AS peId, NULL AS paId, 1 AS fbId
FROM PlaceFeedback
WHERE IsSystem = 0
) src
GROUP BY PlaceId
The idea behind this select, which is very easy to make into a view, is to select items from three tables into one for counting, and then group them all at once.
If you have two active events, one announcement, and three feedbacks for place ID 123, the three inner selects would produce this:
PlaceId peId paId fbId
------- ---- ---- ----
123 1 NULL NULL
123 1 NULL NULL
123 NULL 1 NULL
123 NULL NULL 1
123 NULL NULL 1
123 NULL NULL 1

How to implement a SQL Server query which has several join conditions

I am trying to implement this query but I can’t figure out why I am not getting the result.
Here are the descriptions:
Lets say I have a table call: TableAct
Acct# Date WithdrawAmt DepositAmt
!24455 2012-11-19-00.00.00 1245.77 200.50
125577 2011-02-12-00.00.00 100.98 578.00
Another table TableCustomerOrd:
ID# COrder# CustID Ord_Description VendorType
124455 7712AS 123 1AAA Permanent
125577 9914DL 346 1BBB Partial
... UK1234 111 2HJ5 Permanent'
,,, FR0912 567 5LGY Partial
Then TableCustomerDtls:
CustID Descriptions Delivery Address ZipCode
123 1AAA_BLUESHARE SUCCESSFUL 222 Main St 97002
346 1BBB_CHASE DECLINE 40 West Side 97122
111 2HJ5_CITIBANK SUCCESSFUL ……. …….
567 5LGY_VANGURD DECLINED ---- -----
And table DelivaryFlight:
FlightOrder# FlightCustID FlightDt
7712AS 123 2011-9-29-00.00.00
9914DL 346 2010-11-2-00.00.00
UK1234 111 2012-4-1-00.00.00
FR0912 567 2012-9-11-00.00.00
I want to update TableAct on the following conditions:
TableAct. Acct# = TableCustomerOrd.ID#, AND:
TableCustomerOrd. CustID = TableCustomerDtls.CustID and at the same time, TableCustomerOrd.Ord_Descriptions field should match with TableCustomerDtls. Descriptions field anything before “_” . Therefore ‘1AAA’, ‘2HJ5’ etc. AND:
DelivaryFlight.FlightOrder# = TableCustomerOrd.COrder#, AND: DelivaryFlight.FlightCustID = TableCustomerOrd. CustID. Also TableCustomerDtls. Delivery = ‘SUCCESSFUL’ AND:
DelivaryFlight.FlightOrder# = TableCustomerOrd. COrder#
AND DelivaryFlight.FlightCustID = TableCustomerOrd. CustID
Also TableCustomerDtls. Delivery = ‘DECLINED
Then I want to compare: elivaryFlight.FlightDt > DelivaryFlight.FlightDt.
Basically I need to match table DelivaryFlight columns FlightOrder#, FlightCustID with TableCustomerOrd.
Moreover TableCustomerDtls column Delivery to ck for delivary status such as ‘DECLINED’.
And ‘SUCCESSFUL’ condition and compare ‘SUCCESSFUL’ FlightDt with ‘DECLINED’ FlightDt.
Here's my query but please help me to understand, I am sure this could be done in a better way.
The query is not working:
Update
Set …
FROM TableAct AC
Join TableCustomerOrd CustOd
ON AC.Acct# = CustOd.ID#
Join TableCustomerDtls CDtls
ON CDtls. CustID = CustOd. CustID
AND (CustOd.Ord_Descriptions =
Left(CDtls.Descriptions, LEN(rtrim(CDtls.Descriptions))))
JOIN DelivaryFlight DF
ON DF.FlightOrder# = CustOd.COrder#
AND DF.FlightCustID = CustOd.CustID
AND CDtls.Delivery = ‘SUCCESSFUL’
JOIN DelivaryFlight DF2
ON DF2.FlightOrder# = DF.COrder#
AND DF2.FlightCustID = DF.CustID
AND CDtls.Delivery = ‘DECLINED’
WHERE DelivaryFlight. FlightDt > DelivaryFlight. FlightDt
AND DepositAmt > 100
Your Help will be monumental 'cause my project due end of this week.
Thank you
If I have a complex query like this, I start by creating a "simple" select which produces only the rows to be updated.
It should also return both the update values and the pk for the updated table
It is then (relatively) straight forward to (inner) join this with the table to be updated and do the update remebering to only update matching rows by including
WHERE tblTobeUpdated.pk = SimpleSelect.pk
Hope this helps
I don't have the time to look at this in depth but I suspect you at least want to fix:
WHERE DelivaryFlight. FlightDt > DelivaryFlight. FlightDt
This is a condition that can never be met.
You probably want:
WHERE DF. FlightDt > DF2. FlightDt
it is also useful with these complex queires for an update to be able to see the records that would be updated, so I usually do something like this:
Update
Set …
--Select *
FROM TableAct AC
Then instead of running the update, I run just highlight and run the part that starts with select to see the results and don't test the update until I am sure I am selecting the records I want to select and that the values I will be replacing are correct.
Try breaking your query down, heres a query I wrote today, test each part separately
SELECT
Employee
, Reference
, Payroll
, [Hours] / 60
[Hours]
, [Days]
FROM
(
SELECT
Employee
, Reference
, Payroll
, SUM( Duration ) AS [Hours]
, AvailableID
FROM
(
SELECT
RequirerID
, Duration
, RTRIM( COALESCE(MA.MemberLastName, '')
+ ' ' + COALESCE(MA.MemberFirstName, '')
+ ' ' + COALESCE(MA.MemberInitial, '')) Employee
, COALESCE(MA.Detailref1, '') Reference
, COALESCE(MA.PayrollRef, '') Payroll
, Available.AvailableId
FROM
(
SELECT DISTINCT
RequirerID
, ShiftDate
, CAST(ShiftStart - ShiftEnd - ShiftBreak AS DECIMAL(19,2)) ShiftDuration
, Id RequirementRecordID
FROM
Requirements
WHERE
Requirements.ShiftDate BETWEEN #ParamStartDate
AND #ParamEndDate
AND RequirerID IN (SELECT ID FROM MemberDetails WHERE CompanyID = #ParamCompanyID)
)
R
INNER JOIN
ShiftConfirmed
INNER JOIN
Available
INNER JOIN
MemberDetails MA
ON Available.AvailableID = MA.ID
ON ShiftConfirmed.AvailableRecordID = Available.ID
ON R.RequirementRecordID = ShiftConfirmed.RequirementRecordID
WHERE
R.ShiftDate BETWEEN #ParamStartDate
AND #ParamEndDate
AND COALESCE(ShiftChecked, 0) BETWEEN 0 AND 1
)
ShiftDay
Group By
Employee
, Reference
, Payroll
, AvailableId
) Shifts
INNER JOIN
(
SELECT
COUNT( * ) AS [Days]
, AvailableID
FROM
(
SELECT DISTINCT
R.ShiftDate
, Available.AvailableId
FROM
(
SELECT DISTINCT
ShiftDate
, Id RequirementRecordID
FROM
Requirements
WHERE
Requirements.ShiftDate BETWEEN #ParamStartDate
AND #ParamEndDate
AND RequirerID IN (SELECT ID FROM MemberDetails WHERE CompanyID = #ParamCompanyID)
)
R
INNER JOIN
ShiftConfirmed
INNER JOIN
Available
INNER JOIN
MemberDetails MA
ON Available.AvailableID = MA.ID
ON ShiftConfirmed.AvailableRecordID = Available.ID
ON R.RequirementRecordID = ShiftConfirmed.RequirementRecordID
WHERE
R.ShiftDate BETWEEN #ParamStartDate
AND #ParamEndDate
AND COALESCE(ShiftChecked, 0) BETWEEN 0 AND 1
)
ShiftDay
Group By
AvailableId
) D
ON Shifts.AvailableID = D.AvailableID
WHERE [Hours] > 0
ORDER BY
Employee

#1222 - The used SELECT statements have a different number of columns

Why am i getting a #1222 - The used SELECT statements have a different number of columns
? i am trying to load wall posts from this users friends and his self.
SELECT u.id AS pid, b2.id AS id, b2.message AS message, b2.date AS date FROM
(
(
SELECT b.id AS id, b.pid AS pid, b.message AS message, b.date AS date FROM
wall_posts AS b
JOIN Friends AS f ON f.id = b.pid
WHERE f.buddy_id = '1' AND f.status = 'b'
ORDER BY date DESC
LIMIT 0, 10
)
UNION
(
SELECT * FROM
wall_posts
WHERE pid = '1'
ORDER BY date DESC
LIMIT 0, 10
)
ORDER BY date DESC
LIMIT 0, 10
) AS b2
JOIN Users AS u
ON b2.pid = u.id
WHERE u.banned='0' AND u.email_activated='1'
ORDER BY date DESC
LIMIT 0, 10
The wall_posts table structure looks like id date privacy pid uid message
The Friends table structure looks like Fid id buddy_id invite_up_date status
pid stands for profile id. I am not really sure whats going on.
The first statement in the UNION returns four columns:
SELECT b.id AS id,
b.pid AS pid,
b.message AS message,
b.date AS date
FROM wall_posts AS b
The second one returns six, because the * expands to include all the columns from WALL_POSTS:
SELECT b.id,
b.date,
b.privacy,
b.pid.
b.uid message
FROM wall_posts AS b
The UNION and UNION ALL operators require that:
The same number of columns exist in all the statements that make up the UNION'd query
The data types have to match at each position/column
Use:
FROM ((SELECT b.id AS id,
b.pid AS pid,
b.message AS message,
b.date AS date
FROM wall_posts AS b
JOIN Friends AS f ON f.id = b.pid
WHERE f.buddy_id = '1' AND f.status = 'b'
ORDER BY date DESC
LIMIT 0, 10)
UNION
(SELECT id,
pid,
message,
date
FROM wall_posts
WHERE pid = '1'
ORDER BY date DESC
LIMIT 0, 10))
You're taking the UNION of a 4-column relation (id, pid, message, and date) with a 6-column relation (* = the 6 columns of wall_posts). SQL doesn't let you do that.
(
SELECT b.id AS id, b.pid AS pid, b.message AS message, b.date AS date FROM
wall_posts AS b
JOIN Friends AS f ON f.id = b.pid
WHERE f.buddy_id = '1' AND f.status = 'b'
ORDER BY date DESC
LIMIT 0, 10
)
UNION
(
SELECT id, pid , message , date
FROM
wall_posts
WHERE pid = '1'
ORDER BY date DESC
LIMIT 0, 10
)
You were selecting 4 in the first query and 6 in the second, so match them up.
Beside from the answer given by #omg-ponies; I just want to add that this error also occur in variable assignment. In my case I used an insert; associated with that insert was a trigger. I mistakenly assign different number of fields to different number of variables. Below is my case details.
INSERT INTO tab1 (event, eventTypeID, fromDate, toDate, remarks)
-> SELECT event, eventTypeID,
-> fromDate, toDate, remarks FROM rrp group by trainingCode;
ERROR 1222 (21000): The used SELECT statements have a different number of columns
So you see I got this error by issuing an insert statement instead of union statement. My case difference were
I issued a bulk insert sql
i.e. insert into tab1 (field, ...) as select field, ... from tab2
tab2 had an on insert trigger; this trigger basically decline duplicates
It turns out that I had an error in the trigger. I fetch record based on new input data and assigned them in incorrect number of variables.
DELIMITER ##
DROP TRIGGER trgInsertTrigger ##
CREATE TRIGGER trgInsertTrigger
BEFORE INSERT ON training
FOR EACH ROW
BEGIN
SET #recs = 0;
SET #trgID = 0;
SET #trgDescID = 0;
SET #trgDesc = '';
SET #district = '';
SET #msg = '';
SELECT COUNT(*), t.trainingID, td.trgDescID, td.trgDescName, t.trgDistrictID
INTO #recs, #trgID, #trgDescID, #proj, #trgDesc, #district
from training as t
left join trainingDistrict as tdist on t.trainingID = tdist.trainingID
left join trgDesc as td on t.trgDescID = td.trgDescID
WHERE
t.trgDescID = NEW.trgDescID
AND t.venue = NEW.venue
AND t.fromDate = NEW.fromDate
AND t.toDate = NEW.toDate
AND t.gender = NEW.gender
AND t.totalParticipants = NEW.totalParticipants
AND t.districtIDs = NEW.districtIDs;
IF #recs > 0 THEN
SET #msg = CONCAT('Error: Duplicate Training: previous ID ', CAST(#trgID AS CHAR CHARACTER SET utf8) COLLATE utf8_bin);
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = #msg;
END IF;
END ##
DELIMITER ;
As you can see i am fetching 5 fields but assigning them in 6 var. (My fault totally I forgot to delete the variable after editing.
You are using MySQL Union.
UNION is used to combine the result from multiple SELECT statements into a single result set.
The column names from the first SELECT statement are used as the column names for the results returned. Selected columns listed in corresponding positions of each SELECT statement should have the same data type. (For example, the first column selected by the first statement should have the same type as the first column selected by the other statements.)
Reference: MySQL Union
Your first select statement has 4 columns and second statement has 6 as you said wall_post has 6 column.
You should have same number of column and also in same order in both statement.
otherwise it shows error or wrong data.