Index: src/wp-includes/query.php
===================================================================
--- src/wp-includes/query.php	(revision 28745)
+++ src/wp-includes/query.php	(working copy)
@@ -2116,6 +2116,64 @@
 	}
 
 	/**
+	 * If the passed orderby value is allowed, convert the alias to a properly-prefixed orderby value
+	 *
+	 * @since 4.0.0
+	 *
+	 * @global wpdb $wpdb
+	 * @param string $orderby Alias for the field to order by.
+	 * @return string Table-prefixed value to used in the ORDER clause.
+	 */
+	protected function parse_orderby( $orderby ) {
+		global $wpdb;
+
+		// Used to filter values
+		$allowed_keys = array( 'name', 'author', 'date', 'title', 'modified', 'menu_order', 'parent', 'ID', 'rand', 'comment_count', 'type' );
+		$meta_key = $this->get( 'meta_key' );
+		if ( ! empty( $meta_key ) ) {
+			$allowed_keys[] = $meta_key;
+			$allowed_keys[] = 'meta_value';
+			$allowed_keys[] = 'meta_value_num';
+		}
+
+		if ( ! in_array( $orderby, $allowed_keys ) ) {
+			return;
+		}
+
+		switch ( $orderby ) {
+			case 'menu_order':
+				$orderby = "$wpdb->posts.menu_order";
+				break;
+			case 'ID':
+				$orderby = "$wpdb->posts.ID";
+				break;
+			case 'rand':
+				$orderby = 'RAND()';
+				break;
+			case $meta_key:
+			case 'meta_value':
+				$type = $this->get( 'meta_type' );
+				if ( ! empty( $type ) ) {
+					$meta_type = $this->meta_query->get_cast_for_type( $type );
+					$orderby = "CAST($wpdb->postmeta.meta_value AS {$meta_type})";
+				} else {
+					$orderby = "$wpdb->postmeta.meta_value";
+				}
+				break;
+			case 'meta_value_num':
+				$orderby = "$wpdb->postmeta.meta_value+0";
+				break;
+			case 'comment_count':
+				$orderby = "$wpdb->posts.comment_count";
+				break;
+			default:
+				$orderby = "$wpdb->posts.post_" . $orderby;
+		}
+
+		return $orderby;
+	}
+
+	/**
 	 * Sets the 404 property and saves whether query is feed.
 	 *
 	 * @since 2.0.0
@@ -2640,7 +2698,8 @@
 			$q['order'] = 'DESC';
 
 		// Order by
-		if ( empty($q['orderby']) ) {
+		if ( empty( $q['orderby'] )
+			&& ( ! isset( $this->query['orderby'] ) || ! is_array( $this->query['orderby'] ) ) ) {
 			$orderby = "$wpdb->posts.post_date " . $q['order'];
 		} elseif ( 'none' == $q['orderby'] ) {
 			$orderby = '';
@@ -2649,59 +2708,37 @@
 		} elseif ( $q['orderby'] == 'post_parent__in' && ! empty( $post_parent__in ) ) {
 			$orderby = "FIELD( {$wpdb->posts}.post_parent, $post_parent__in )";
 		} else {
-			// Used to filter values
-			$allowed_keys = array( 'name', 'author', 'date', 'title', 'modified', 'menu_order', 'parent', 'ID', 'rand', 'comment_count', 'type' );
-			if ( !empty($q['meta_key']) ) {
-				$allowed_keys[] = $q['meta_key'];
-				$allowed_keys[] = 'meta_value';
-				$allowed_keys[] = 'meta_value_num';
-			}
-			$q['orderby'] = urldecode($q['orderby']);
-			$q['orderby'] = addslashes_gpc($q['orderby']);
-
 			$orderby_array = array();
-			foreach ( explode( ' ', $q['orderby'] ) as $i => $orderby ) {
-				// Only allow certain values for safety
-				if ( ! in_array($orderby, $allowed_keys) )
-					continue;
+			if ( is_array( $q['orderby'] ) ) {
+				foreach ( $q['orderby'] as $_orderby => $order ) {
+					$orderby = addslashes_gpc( urldecode( $_orderby ) );
+					$parsed = $this->parse_orderby( $orderby );
+					if ( ! $parsed ) {
+						continue;
+					}
+					$orderby_array[] = implode( ' ', array( $parsed, $order ) );
+				}
+				$orderby = implode( ', ', $orderby_array );
+			} else {
+				$q['orderby'] = urldecode($q['orderby']);
+				$q['orderby'] = addslashes_gpc($q['orderby']);
 
-				switch ( $orderby ) {
-					case 'menu_order':
-						$orderby = "$wpdb->posts.menu_order";
-						break;
-					case 'ID':
-						$orderby = "$wpdb->posts.ID";
-						break;
-					case 'rand':
-						$orderby = 'RAND()';
-						break;
-					case $q['meta_key']:
-					case 'meta_value':
-						if ( isset( $q['meta_type'] ) ) {
-							$meta_type = $this->meta_query->get_cast_for_type( $q['meta_type'] );
-							$orderby = "CAST($wpdb->postmeta.meta_value AS {$meta_type})";
-						} else {
-							$orderby = "$wpdb->postmeta.meta_value";
-						}
-						break;
-					case 'meta_value_num':
-						$orderby = "$wpdb->postmeta.meta_value+0";
-						break;
-					case 'comment_count':
-						$orderby = "$wpdb->posts.comment_count";
-						break;
-					default:
-						$orderby = "$wpdb->posts.post_" . $orderby;
+				foreach ( explode( ' ', $q['orderby'] ) as $i => $orderby ) {
+					$parsed = $this->parse_orderby( $orderby );
+					// Only allow certain values for safety
+					if ( ! $parsed ) {
+						continue;
+					}
+
+					$orderby_array[] = $parsed;
 				}
+				$orderby = implode( ' ' . $q['order'] . ', ', $orderby_array );
 
-				$orderby_array[] = $orderby;
+				if ( empty( $orderby ) )
+					$orderby = "$wpdb->posts.post_date ".$q['order'];
+				else
+					$orderby .= " {$q['order']}";
 			}
-			$orderby = implode( ' ' . $q['order'] . ', ', $orderby_array );
-
-			if ( empty( $orderby ) )
-				$orderby = "$wpdb->posts.post_date ".$q['order'];
-			else
-				$orderby .= " {$q['order']}";
 		}
 
 		// Order search results by relevance only when another "orderby" is not specified in the query.
Index: tests/phpunit/tests/post/query.php
===================================================================
--- tests/phpunit/tests/post/query.php	(revision 28745)
+++ tests/phpunit/tests/post/query.php	(working copy)
@@ -761,4 +761,33 @@
 		$q3 = new WP_Query( array( 'post_status' => array( 'any', 'auto-draft' ) ) );
 		$this->assertNotContains( "post_status <> 'auto-draft'", $q3->request );
 	}
+
+	/**
+	 *
+	 * @ticket 17065
+	 */
+	function test_orderby_array() {
+		global $wpdb;
+
+		$q1 = new WP_Query( array(
+			'orderby' => array(
+				'type' => 'DESC',
+				'name' => 'ASC'
+			)
+		) );
+		$this->assertContains(
+			"ORDER BY $wpdb->posts.post_type DESC, $wpdb->posts.post_name ASC",
+			$q1->request
+		);
+
+		$q2 = new WP_Query( array( 'orderby' => array() ) );
+		$this->assertNotContains( 'ORDER BY', $q2->request );
+		$this->assertNotContains( 'ORDER', $q2->request );
+
+		$q3 = new WP_Query( array( 'post_type' => 'post' ) );
+		$this->assertContains(
+			"ORDER BY $wpdb->posts.post_date DESC",
+			$q3->request
+		);
+	}
 }
\ No newline at end of file
