Search code examples
sqlmfcjet

Can these queries be combined into one?


I have this MFC code to extract a list of names from a Microsoft Access Database:

// Extracts all the brothers from the specified tables into the passed in array
void CPTSDatabase::BuildBrothersArray(CStringArray &rAryStrBrothers)
{
    CWaitCursor         wait;
    CMapStringToString  mapStrBrothers;
    CStringArray        aryStrQueries, aryStrFields;
    CString             strText, strBrother;
    POSITION            sPos;
    int                 iTable, iNumTables;

    rAryStrBrothers.RemoveAll();

    if (m_dbDatabase.IsOpen())
    {
        strText.Format(_T("SELECT * FROM [Congregation Speakers] ")
                _T("WHERE [Congregation]='%s' ORDER BY Speaker"), (LPCTSTR)GetLocalCongregation());
        aryStrQueries.Add(_T("SELECT * FROM Brothers WHERE BrotherChairman=-1"));
        aryStrQueries.Add(_T("SELECT * FROM Brothers WHERE BrotherReader=-1"));
        aryStrQueries.Add(_T("SELECT * FROM Brothers WHERE BrotherConductorWT=-1"));
        aryStrQueries.Add(strText);

        aryStrFields.Add(_T("BrotherName"));
        aryStrFields.Add(_T("BrotherName"));
        aryStrFields.Add(_T("BrotherName"));
        aryStrFields.Add(_T("Speaker"));

        iNumTables = 4;
        for (iTable = 0; iTable < iNumTables; iTable++)
        {
            GetBrotherData(aryStrQueries[iTable], aryStrFields[iTable], mapStrBrothers);
        }

        sPos = mapStrBrothers.GetStartPosition();
        while (sPos != NULL)
        {
            mapStrBrothers.GetNextAssoc(sPos, strText, strBrother);
            rAryStrBrothers.Add(strBrother);
        }
    }
}

// Extracts all the brothers from the specified table / field
// A map is used so that we end up with a list of unique brothers
void CPTSDatabase::GetBrotherData(CString strSQL, CString strField,
                                  CMapStringToString &rMapBrothers)
{
    CRecordset  *pRecordset = NULL;
    CString     strBrother;

    if (!m_dbDatabase.IsOpen())
        return;

    pRecordset = new CRecordset(&m_dbDatabase);
    if (pRecordset != NULL)
    {
        pRecordset->Open(CRecordset::snapshot,(LPCTSTR)strSQL);
        while (!pRecordset->IsEOF() )
        {
            pRecordset->GetFieldValue(strField, strBrother);
            rMapBrothers.SetAt(strBrother, strBrother);
            pRecordset->MoveNext();
        }

        pRecordset->Close();
        delete pRecordset ;
    }
}

// Locates the Congregation that has the "Local" flag set
// The local congregation is the home congregation
CString CPTSDatabase::GetLocalCongregation()
{
    CRecordset  *pCongs = NULL;
    CString     strCong, strQuery;

    if (m_dbDatabase.IsOpen())
    {
        pCongs = new CRecordset( &m_dbDatabase );
        if (pCongs != NULL)
        {
            strQuery = _T("SELECT * FROM [Congregations] WHERE [Local] = 1");
            pCongs->Open( CRecordset::snapshot, (LPCTSTR)strQuery );
            if( pCongs->GetRecordCount() > 0 )
            {
                pCongs->GetFieldValue(_T("Congregations"), strCong);
            }
        }
        pCongs->Close();
        delete pCongs ;
    }

    return strCong;
}

As you can see, I have several tables where I am doing searches and added any unique names to a list. I don't want to overcomplicate anything but is it possible to combine this is a single query and pull out one CRecordSet that has one unique list of names?

UPDATE

Having done a bit more research it seems I need a UNION. So I need to combine the distinct results from these queries as a single column:

SELECT Speaker FROM [Congregation Speakers] WHERE [Congregation]='xyz'
SELECT BrotherName FROM Brothers WHERE BrotherChairman=-1")
SELECT BrotherName FROM Brothers WHERE BrotherReader=-1
SELECT BrotherName FROM Brothers WHERE BrotherConductorWT=-1

The results should be sorted A to Z.

UPDATE

Having done a bit more research it seems I need a UNION. So I need to combine the distinct results from these queries as a single column:

SELECT Speaker FROM [Congregation Speakers] WHERE [Congregation]='xyz'
SELECT BrotherName FROM Brothers WHERE BrotherChairman=-1")
SELECT BrotherName FROM Brothers WHERE BrotherReader=-1
SELECT BrotherName FROM Brothers WHERE BrotherConductorWT=-1

The results should be sorted A to Z.

Update

I have tried:

void CPTSDatabase::BuildBrothersArray(CStringArray &rAryStrBrothers)
{
    CWaitCursor         wait;
    CRecordset *pRecordset = nullptr;
    if (!m_dbDatabase.IsOpen())
        return;

    pRecordset = new CRecordset(&m_dbDatabase);
    if (pRecordset != nullptr)
    {
        CString strSQL = _T(""), strName = _T("");

        strSQL.Format(_T("WITH CTE(Name) AS(")
            _T("SELECT Speaker FROM [Congregation Speakers] WHERE [Congregation] = '%s' ")
            _T("UNION ")
            _T("SELECT BrotherName FROM Brothers WHERE BrotherChairman = -1 OR BrotherReader = -1 OR BrotherConductorWT = -1)")
            _T(") ")
            _T("SELECT Name FROM CTE ORDER BY Name ASC"), (LPCTSTR)GetLocalCongregation());

        try
        {
            pRecordset->Open(CRecordset::snapshot, (LPCTSTR)strSQL);
            while (!pRecordset->IsEOF())
            {

                pRecordset->GetFieldValue(_T("Name"), strName);
                rAryStrBrothers.Add(strName);
                pRecordset->MoveNext();
            }

            pRecordset->Close();
        }
        catch (CDBException* e)
        {
            TCHAR szError[_MAX_PATH];
            e->GetErrorMessage(szError, _MAX_PATH);
            AfxMessageBox(szError);

        }

        delete pRecordset;
    }
}

But I get this error:

Error

UPDATE 2

This seems to work fine:

strSQL.Format(_T("SELECT Speaker AS Name FROM [Congregation Speakers] WHERE [Congregation] = '%s' ")
    _T("UNION ")
    _T("SELECT BrotherName AS Name FROM Brothers WHERE BrotherChairman = -1 OR BrotherReader = -1 OR BrotherConductorWT = -1 ORDER BY Name ASC"), (LPCTSTR)GetLocalCongregation());

Solution

  • Based on the original answer, this is what works for my CRecordSet:

    strSQL.Format(
        _T("SELECT Speaker AS Name FROM [Congregation Speakers] WHERE [Congregation] = '%s' ")
        _T("UNION ")
        _T("SELECT BrotherName AS Name FROM Brothers WHERE ")
        _T("BrotherChairman = -1 OR ")
        _T("BrotherReader = -1 OR ")
        _T("BrotherConductorWT = -1 OR ")
        _T("BrotherHospitality = -1 OR ")
        _T("BrotherInterpreter = -1 OR ")
        _T("BrotherMiscellaneous = -1 ")
        _T("ORDER BY Name ASC"), (LPCTSTR)GetLocalCongregation());