这些查询可以合并为一个吗?

Can these queries be combined into one?

我有这个 MFC 代码可以从 Microsoft Access 数据库中提取姓名列表:

// 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;
}

如您所见,我在多个表中进行搜索并将所有唯一名称添加到列表中。我不想让任何事情过于复杂,但是否可以将这是一个单一的查询组合起来,并提取一个具有唯一名称列表的 CRecordSet

更新

经过更多研究,我似乎需要一个 UNION。所以我需要将这些查询的不同结果合并为一个列:

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

结果应按 A 到 Z 排序。

更新

经过更多研究,我似乎需要一个 UNION。所以我需要将这些查询的不同结果合并为一个列:

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

结果应按 A 到 Z 排序。

更新

我试过:

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;
    }
}

但是我得到这个错误:

更新 2

这似乎工作正常:

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());

没试过,很快就破解了:

WITH CTE(Name) AS (
  SELECT Speaker FROM [Congregation Speakers] WHERE [Congregation]='xyz'
  UNION
  SELECT BrotherName FROM Brothers WHERE BrotherChairman=-1 OR BrotherReader=-1 OR BrotherConductorWT=-1
)
SELECT Name FROM CTE ORDER BY Name ASC

并且请不要从用户输入中动态组成 SQL 字符串,而是使用参数化查询,否则您将容易受到 SQL 注入攻击。

根据原始答案,这对我的 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());