Click here to Skip to main content
15,896,434 members
Articles / Desktop Programming / MFC

SQL WHERE - the easy way

Rate me:
Please Sign up or sign in to vote.
4.10/5 (12 votes)
18 Feb 2002Ms-PL2 min read 123.6K   1.5K   34  
Creating SQL WHERE clauses can be a pain. This class grants relief.
#if !defined(_CWHERE_INCLUDED_)
#define _CWHERE_INCLUDED

//
// Simple class to encapsulate building a SQL WHERE clause
//
// (C) 2002 Andreas Saurwein, saurwein@uniwares.com
// You are granted the right to use, reuse and modify the source in any
// way except commercially selling the class.
//
//
// usage: for one-expression type wheres use the constructor version
//
// CWhere mywhere(L"myfield", 100, L">") or
// CWhere mywhere(fieldvar, L"text")
//
// and/or contruct the clause using Add()
//
// mywhere.Add(L"nullfield");
// mywhere.AddBetween(L"complexfield", date1, date2);

//
// Add with only the fieldname parameter will add a "field IS NULL" clause
// Add with fieldname and one parameter will add a "field=param" clause
// Add with fieldname, one variant parameter and a wchar param will add 
//    "field"+wchar param+variant param
// e.g. Add(L"field", L"test%", L" LIKE ") gives "field LIKE 'test%'"
//
// additionally you can create clauses with OR / AND logic by using the 
// logical operators &, |, &= and |= 
//
// The operators & and &= will create a AND clause from the supplied clauses 
// while the operators | and |= will create a OR clause.
//
// Sample:
// 	 You want "WHERE (field1 OR field2) AND (field3 AND field4)" where 
// fieldX is each a CWhere clause
// 
// CWhere another;
// another = field1 | field2;		// logical operation OR
// another &= field3 & field4;		// logical operation AND
//
// since the data parameters are variants you can stuff in whatever you can 
// stuff into a variant and can be converted to VT_BSTR.
//

#include <comdef.h>

class CWhere
{
public:
	CWhere(){};

	//
	// the plain PSTR variations
	//
	
	// field IS NULL
	CWhere(TCHAR* field)				
		{ Add(field);	};

	// "field exp vt", default to "field = vt"
	// use the template version for "normal" datatypes like int, long, float, double, etc.
	template <class T> CWhere(const TCHAR* field, T vt, const TCHAR* exp=_T("="))	
		{ _variant_t v((T)vt); Add(field, v, exp); }
	
	// field BETWEEN vt1 AND vt2
	/* 
	// unfortunately this doesnt work. the compiler cant resolve the ambiguity.

	template <class T1, class T2> CWhere(const TCHAR* field, const T1 vt1, const T2 vt2)	
		{ _variant_t v1((T1)vt1); _variant_t v2((T2)vt2); Add(field, v1, v2);	}
	*/
	
	//
	// the CString variations
	//
	CWhere(const CString& field)	
		{ Add(field);	};
	template <class T> CWhere(const CString& field, T vt, const TCHAR* exp=_T("="))	
		{ _variant_t v((T)vt) ; Add(field, v, exp); }
	/*
	template <class T1, class T2> CWhere(const CString& field, const T1 vt1, const T2 vt2)	
		{ _variant_t v1((T1)vt1); _variant_t v2((T2)vt2); Add(field, v1, v2);	}
	*/
	
	enum
	{ 
		cwNone, cwOr, cwAnd, cwOrGroup, cwAndGroup
	};
	
	CWhere(const CWhere& a)
	{
		this->operator=(a);	// assign ourself a copy of it
	}

	CWhere& operator=(const CWhere& a)
	{
		if(&a != this)
		{
			m_sWhere.RemoveAll();
			m_sWhere.Append(a.m_sWhere);
			
			m_nOp.RemoveAll();
			m_nOp.Append(a.m_nOp);
		}
		return *this;
	}
	

	CWhere& operator&(const CWhere& a)	{ return this->operator&=(a); }
	CWhere& operator|(const CWhere& a)	{ return this->operator|=(a); }
		
	CWhere& operator&=(const CWhere& a)
	{
		if(m_sWhere.GetSize() == 0)
			return this->operator=(a);

		if(m_sWhere.GetSize() > 1)
		{
			// wrap existing ones into brackets, add "AND"
			m_sWhere.InsertAt(0, CString(_T("("))); m_nOp.InsertAt(0, cwAndGroup);
			m_nOp[m_nOp.GetSize()-1] = cwNone;	// remove it, its the last in its group
			m_sWhere.Add(CString(_T(")"))); m_nOp.Add(cwAnd);
			
			// wrap new ones into brackets
			m_sWhere.Add(CString(_T("("))); m_nOp.Add(cwAndGroup);

			m_sWhere.Append(a.m_sWhere);
			m_nOp.Append(a.m_nOp);
			m_nOp[m_nOp.GetSize()-1] = cwNone;	// remove it, its the last in its group
			
			m_sWhere.Add(CString(_T(")"))); m_nOp.Add(cwAndGroup);
		}
		else if(m_sWhere.GetSize() == 1)
		{
			// just add it and set the default operation
			m_nOp[0] = cwAnd;
			m_sWhere.Append(a.m_sWhere);
			m_nOp.Append(a.m_nOp);
		}
		return *this;
	}

	CWhere& operator|=(const CWhere& a)
	{
		if(m_sWhere.GetSize() == 0)
			return this->operator=(a);
		
		if(m_sWhere.GetSize() > 1)
		{
			// wrap existing ones into brackets, add "AND"
			m_sWhere.InsertAt(0, CString(_T("("))); m_nOp.InsertAt(0, cwOrGroup);
			m_nOp[m_nOp.GetSize()-1] = cwNone;	// remove it, its the last in its group
			m_sWhere.Add(CString(_T(")"))); m_nOp.Add(cwOr);
			
			// wrap new ones into brackets
			m_sWhere.Add(CString(_T("("))); m_nOp.Add(cwOrGroup);
			
			m_sWhere.Append(a.m_sWhere);
			m_nOp.Append(a.m_nOp);
			m_nOp[m_nOp.GetSize()-1] = cwNone;	// remove it, its the last in its group
			
			m_sWhere.Add(CString(_T(")"))); m_nOp.Add(cwOrGroup);
		}
		else if(m_sWhere.GetSize() == 1)
		{
			// just add it and set the default operation
			m_nOp[0] = cwOr;
			m_sWhere.Append(a.m_sWhere);
			m_nOp.Append(a.m_nOp);
		}
	
		return *this;
	}
	
	virtual ~CWhere(){};


	// WHERE field IS NULL
	void Add(const TCHAR* field)	
	{	
		CString w(_T("("));

		w += field;
		w += _T(" IS NULL");
		w += _T(")");

		m_sWhere.Add(w);
		m_nOp.Add(cwAnd);
	}
	
	// WHERE (field = vt)
	void Add(const TCHAR* field, const _variant_t& vt, const TCHAR* exp=_T("="))
	{
		CString w(_T("("));
		
		w += field;
		w += _T(" ");
		w += exp;		// to be on  the save side we enclose it in spaces
		w += _T(" ");
		
		if(vt.vt == VT_BSTR)
		{
#if defined(_UNICODE) || defined(UNICODE)
			w += _T("N'");
#else
			w += _T("'");
#endif
			w += _bstr_t(vt).operator const TCHAR*();
			w += _T("'");
		}
		else
		{
			try
			{	_variant_t vtx(vt);	vtx.ChangeType(VT_BSTR);
				w += _bstr_t(vtx).operator const TCHAR*();
			}
			catch(...)
			{
				w += _T("<conversion failed>");
			}
		}
		w += _T(")");

		m_sWhere.Add(w);
		m_nOp.Add(cwAnd);
	}


	// WHERE field BETWEEN vt1 AND vt2
	void AddBetween(const TCHAR* field, const _variant_t& vt1, const _variant_t& vt2)
	{
		CString w(_T("("));
		
		w += field;
		w += _T(" BETWEEN ");
		
		if(vt1.vt == VT_BSTR)
		{
#if defined(_UNICODE) || defined(UNICODE)
			w += _T("N'");
#else
			w += _T("'");
#endif
			w += _bstr_t(vt1).operator const TCHAR*();
			w += _T("'");
		}
		else
		{
			try			{_variant_t vtx(vt1); vtx.ChangeType(VT_BSTR); w += _bstr_t(vtx).operator const TCHAR*(); }
			catch(...)	{ w += _T("<conversion failed>");}
		}
		w+= _T(" AND ");
		if(vt2.vt == VT_BSTR)
		{
#if defined(_UNICODE) || defined(UNICODE)
			w += _T("N'");
#else
			w += _T("'");
#endif
			w += _bstr_t(vt2).operator const TCHAR*();
			w += _T("'");
		}
		else
		{
			try			{_variant_t vtx(vt2); vtx.ChangeType(VT_BSTR); w += _bstr_t(vtx).operator const TCHAR*();}
			catch(...)	{ w += _T("<conversion failed>");}
		}
		
		w += _T(")");

		m_sWhere.Add(w);
		m_nOp.Add(cwAnd);
	}
	
	// get WHERE clause without the " WHERE "
	CString GetClausePlain()
	{
		int and = m_sWhere.GetSize()-1;

		if(and < 0)
			return CString();
		
		CString whereclause;
		
		for(int i = 0; i <= and; i++)
		{
			whereclause += m_sWhere[i];
			if(i < and)
			{
				switch(m_nOp[i]) 
				{
					case cwAnd:
						whereclause += _T(" AND ");
						break;
					case cwOr:
						whereclause += _T(" OR ");
						break;
					case cwAndGroup:
						break;
					case cwOrGroup:
						break;
					default:
						break;
				}
			}
		}
		
		whereclause += _T(" ");
#ifdef _VERBOSE_DEBUG
		OutputDebugString(whereclause);
		OutputDebugString(_T("\r\n"));
#endif
		return whereclause;
	}

	// get the complete clause including " WHERE "
	CString GetClause()
	{
		return CString(_T(" WHERE ")) + GetClausePlain();
	}
	
	
private:
	CStringArray	m_sWhere;
	CWordArray		m_nOp;
};

#endif

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)


Written By
Software Developer (Senior)
Portugal Portugal
Software Smith, Blacksmith, Repeat Founder, Austrian, Asgardian.

Comments and Discussions